我在C编程语言的一道客观题中遇到了这个问题。以下代码的输出应该是0 2
,但我不明白为什么。
请解释初始化过程。以下是代码:
#include <stdio.h>
int main()
{
union a
{
int x;
char y[2];
};
union a z = {512};
printf("\n%d %d", z.y[0], z.y[1]);
return 0;
}
我在C编程语言的一道客观题中遇到了这个问题。以下代码的输出应该是0 2
,但我不明白为什么。
请解释初始化过程。以下是代码:
#include <stdio.h>
int main()
{
union a
{
int x;
char y[2];
};
union a z = {512};
printf("\n%d %d", z.y[0], z.y[1]);
return 0;
}
int
的大小为4个字节(32位)
,char
的大小为1个字节(8位)
,并且整数以二进制补码形式表示。一个union
的大小只有它最大成员的大小,并且所有成员都指向这个确切的内存区域。现在,您将一个整数值512
写入这个内存。
512的二进制表示为1000000000
。
或者以32位二进制补码形式表示:
00000000 00000000 00000010 00000000
。
现在将其转换为小端表示,你会得到:
00000000 00000010 00000000 00000000
|______| |______|
| |
y[0] y[1]
现在看一下上面的内容,当你使用 char 数组的索引访问它时会发生什么。
因此,y[0]
是 00000000
,即 0
,
而 y[1]
是 00000010
,即 2
。
y
,超出其边界(例如 y[3]
)是否会像普通数组访问一样产生未定义行为? - nanofaradsizeof(int)
< 3 * sizeof(char)
,否则 y[3]
是合法的,因为它只是 *(y + 3)
,而 y + 3
是由联合占用的合法地址。 - Arjun Sreedharan0 2
?”,我认为OP在这里的评论证实了我的假设。 - Arjun Sreedharan联合分配的内存大小是联合中最大类型的大小,在本例中为int
。假设您系统上的int
大小为2字节,则
512
将变为0x200
。
表示形式如下:
0000 0010 0000 0000
| | |
-------------------
Byte 1 Byte 0
在小端系统上,第一个字节是0
,第二个字节是2
。
char
在所有系统上都是一个字节。
因此,访问z.y[0]
和z.y[1]
是按字节访问。
z.y[0] = 0000 0000 = 0
z.y[1] = 0000 0010 = 2
我只是告诉你内存分配和值的存储方式。您需要考虑以下几点,因为输出取决于它们。
要注意的事项:
sizeof(int)
很重要,这将在系统之间变化。注:联合体中两个成员占用的内存相同。
union a z={0x12345678}; printf("\n%x %x",z.y[0],z.y[1]);
输出将更加直观。 - Jabberwocky联合类型描述了一组有重叠非空成员对象,每个对象都有一个可选的名称和可能不同的类型。
编译器只为最大的成员分配足够的空间,在该空间内它们会互相覆盖。在您的情况下,内存被分配给 int
数据类型(假设为4字节)。这行代码:
union a z = {512};
将初始化联合体z
的第一个成员,即x
变为512
。在二进制中,它表示为32位机器上的0000 0000 0000 0000 0000 0010 0000 0000
。
这个内存表示方式取决于机器架构。在32位机器上,它可能是这样的(将最低有效字节存储在最小地址中-- 小端序)
Address Value
0x1000 0000 0000
0x1001 0000 0010
0x1002 0000 0000
0x1003 0000 0000
或者像(将最高有效字节存储在最小地址--大端模式)
Address Value
0x1000 0000 0000
0x1001 0000 0000
0x1002 0000 0010
0x1003 0000 0000
z.y[0]
将会访问地址为 0x1000
的内容,而 z.y[1]
将会访问地址为 0x1001
的内容,这些内容将取决于上述表示方式。
看起来你的机器支持小端表示法,因此 z.y[0] = 0
且 z.y[1] = 2
,输出结果将为 0 2
。
但是,你需要注意,在第 6.5.2.3 节的脚注 95 中指出:
如果用于读取联合对象内容的成员与最后一次用于在对象中存储值的成员不同,则该值的对象表示的适当部分将被重新解释为新类型中的对象表示,如 6.2.6 中所述(有时称为“类型拼接”)。这可能会导致陷阱表示。
union a的大小=4字节
。2000-> int x,y [0]的最后(第4个/最不重要/最右边)个字节
2001-> int x,y [1]的第3个字节
2002-> int x的第2个字节
2003-> int x的第1个字节(最重要)
M[2000] = 0x00
M[2001] = 0x02
M[2002] = 0x00
M[2003] = 0x00
因此,当您打印 y[0] 和 y[1] 时,它将打印数据 M[2000] 和 M[2001],分别为十进制的 0 和 2。
对于自动(非静态)成员,初始化与赋值相同:
union a z;
z.x = 512;