具有union的C代码中出现意外输出
我不理解以下C代码中的输出:具有union的C代码中出现意外输出,c,unions,C,Unions,我不理解以下C代码中的输出: #include <stdio.h> int main() { union U { int i; char s[3]; } u; u.i=0x3132; printf("%s", u.s); return 0; } 初始内存为32位,是0x3132的二进制值,即 0000 0000 0011 0001 0011 0010 如果0x3132的最后三个字节是不带前导零的s值,则s[0]=0011,
#include <stdio.h>
int main()
{
union U
{
int i;
char s[3];
} u;
u.i=0x3132;
printf("%s", u.s);
return 0;
}
初始内存为32位,是0x3132的二进制值,即
0000 0000 0011 0001 0011 0010
如果0x3132的最后三个字节是不带前导零的s值,则s[0]=0011,s[1]=0001,s[2]=0011
这给出了s=0011 0001 0011=787的值
问题:为什么输出是21而不是787?值0x3132在内存中表示为:0x32、0x31、0x0、0x0,因为字节顺序是小端的 printf调用打印出由联合的成员表示的字符串。字符串逐字节打印出来。首先是0x32,然后是0x31,它们是字符“2”和“1”的ascii值。然后打印停止,因为第三个元素是空字符:0x0
注意,int的表示是实现定义的,可能不包含4个字节,并且可能有填充。因此,union s的成员可能不代表字符串,在这种情况下,使用%s说明符调用printf将导致未定义的行为。这意味着您的机器是little endian,因此字节以相反的顺序存储,如下所示:
32 31 00 00
#include <stdio.h>
int main( void )
{
union U
{
int i;
char s[3];
} u;
u.i=0x3132;
printf("%s", u.s);
printf( "%8x\n", (unsigned)u.i);
}
所以:s[0]=0x32,s[1]=0x31,s[2]=0x00
即使从理论上讲,使用%s打印字符数组是未定义的行为,它也会打印0x32字符“2”,0x31字符“1”,然后停止0x00。首先查看此代码示例:
#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>
int main()
{
union{
int32_t i32;
uint32_t u32;
int16_t i16[2];
uint16_t u16[2];
int8_t i8[4];
uint8_t u8[4];
} u;
u.u8[3] = 52;
u.u8[2] = 51;
u.u8[1] = 50;
u.u8[0] = 49;
printf(" %d %d %d %d \n", u.u8[3], u.u8[2], u.u8[1], u.u8[0]); // 52 51 50 49
printf(" %x %x %x %x \n", u.u8[3], u.u8[2], u.u8[1], u.u8[0]); // 34 33 32 31
printf(" 0x%x \n", u.i32); // 0x34333231
return 0;
}
如图8所示:
那么输出是:
52 51 50 49
并读作int32\t:
那么输出是:
0x34333231
因此,正如您在本示例中看到的,代码联合使用许多名称/类型共享一个内存位置
在示例代码中,u.i=0x3132;这会在u.i内存中写入0x3132,这取决于您的系统的endianness,这里是小endian,然后您要求printf%s,u.s;从编译器中,u.s是char类型的数组,表示指向char类型的常量指针,所以这个printf%s,u.s;will读取u.s[0]并将其打印到输出标准输出上,然后读取u.s[1]并将其打印到输出标准输出上,依此类推,直到其中一个u.s[i]为零。
这就是您的代码所做的,因此,如果u.s[0]、u.s[1]、u.s[2]、u.s[3]中没有一个不为零,则将读取联合外部的内存,直到找到一个零或发生系统内存故障错误。如果您这样编写代码:
32 31 00 00
#include <stdio.h>
int main( void )
{
union U
{
int i;
char s[3];
} u;
u.i=0x3132;
printf("%s", u.s);
printf( "%8x\n", (unsigned)u.i);
}
然后您会看到u.i的内容是0x0000000000003132,由于Endianness,它实际上会存储为:0x32310000000000
并且0x00不是可打印字符,因此第二次调用printf的输出是3132,正如您所期望的那样
ascii字符1为0x31,ascii字符2为0x32,第一个0x00停止%s操作,因此第一个printf输出21。u.s不是字符串,不能使用%s打印。这在技术上是未定义的行为。已经说过,0x31==49==‘1’和0x32==50==‘2’,希望对你们来说是清楚的。最后两组8位在小字节0011 0010和0011 0001中,在十六进制中是32和31,在ASCII中是“2”和“1”。假设siZeFoT是没有任何填充位和字节序是小字节的,和ascii编码,那么这个行为就被定义了。为什么你认为应该是787呢?在设置int字段中位的预期顺序时,需要考虑Endianness。注意:当%s格式说明符看到一个0x00字符时,它会停止。这一定是意外按下了downvote按钮,请编辑您的帖子,以便我可以将其更改为upvote=很抱歉=
#include <stdio.h>
int main( void )
{
union U
{
int i;
char s[3];
} u;
u.i=0x3132;
printf("%s", u.s);
printf( "%8x\n", (unsigned)u.i);
}