联合体和结构体初始化

15

我偶然发现了一段基于C语言联合体的代码。以下是代码:

    union    {  
        struct  {  
            char ax[2];  
            char ab[2];  
        } s;  
        struct  {  
            int a;  
            int b;  
        } st;  
    } u ={12, 1}; 

    printf("%d %d", u.st.a, u.st.b);  

我不太理解为什么输出是268 0,值是如何初始化的?这里联合体起作用了吗?输出难道不应该是12 1吗?如果有人可以详细解释一下正在发生什么,那就太好了。

我正在使用32位处理器和Windows 7操作系统。


1
那么初始化器怎么知道你想要初始化 st 而不是 s 呢? - Christian Rau
13
如果我早知道,这个问题就不会存在了。 - h4ck3d
5个回答

19
代码并没有执行你所想的操作。花括号初始化会将第一个联合体成员,即 `u.s` 进行初始化。然而,现在初始化器不完整,缺少花括号,因为 `u.s` 包含两个数组。应该这样做: `u = { { {'a', 'b'}, { 'c', 'd' } } };`
你应该总是编译时带上所有警告,一个好的编译器应该会告诉你出了什么问题。比如说,GCC 会显示以下消息:`missing braces around initialiser (near initialisation for ‘u.s’)`和`missing initialiser (near initialisation for ‘u.s.ab’)`。非常有用。
在C99中,你可以利用命名成员初始化来初始化第二个联合体成员:`u = { .st = {12, 1} };`(顺便说一下,这在 C++ 中不可能)。第一种情况对应的语法是 `u = { .s = { {'a', 'b'}, { 'c', 'd' } } };`,这可以说更加明确易读!

2
实际上,您需要两个额外的大括号(一个用于联合,一个用于结构体,一个用于数组):u = {{{'a','b'},{'c','d'}}} - Adam Rosenfield
@KerrekSB 谢谢。可能是最好的回答了。您能否解释一下,既然初始化问题已经解决,为什么输出现在是“268 0”呢? - h4ck3d
@KerrekSB 谢谢,我想我明白了,如果我错了请纠正我:位被设置为 00000001 00001100 = 256+8+4 = 268。对吗?但是为什么 u.st.b = 0? - h4ck3d
@NiteeshMehra:不,它必须是小端序才有意义。至于其他方面:正如我所说,需要很多运气。这是未定义的行为,任何事情都可能发生。只是碰巧在那一点上内存被清零了。 - Kerrek SB
3
这是发生的事情。在小端序下,s.ax[2]和s.ab[2]被打包成4个字节。您的初始化将s.ax[0]初始化为12(0x0C),将s.ax[1]初始化为1(0x01)。其他所有内容均初始化为0。(我不知道这是否是“规范”行为,但这是我所期望发生的。)假设您正在使用32位整数,则结果数据布局如下(最低字节在左侧):0C 01 00 00 00 00 00 00。因此,当您打印第一个4字节整数时,您得到0x0000010C或268,当您打印第二个4字节整数时,您得到0x00000000,即神秘的0。 - Andrew Cottrell
显示剩余5条评论

6
你的代码使用联合体的默认初始化器,即其第一个成员。12和1都会进入ax的字符中,因此你看到的结果是非常依赖于编译器的。
如果你想通过第二个成员(st)进行初始化,你可以使用指定的初始化器:
union {  
    struct {  
        char ax[2];  
        char ab[2];  
    } s;  
    struct {  
        int a;  
        int b;  
    } st;  
} u ={ .st = {12, 1}}; 

5

这段代码将u.s.ax[0]设置为12,将u.s.ax[1]设置为1。 u.s.axu.st.a重叠,因此u.st.a的最低有效字节设置为12,最高有效字节设置为1(所以您必须在运行小端架构),得到一个值为0x010C或268。


为什么是 0x010C?不应该是 0x001C 吗? - h4ck3d
谢谢,我觉得我明白了,如果我错了请纠正:这些位像这样设置00000001 00001100= 256+8+4 = 268。对吗?但是为什么u.st.b等于0呢? - h4ck3d
1
是的 - 00000001 000011000x010C。因为只提供了两个值并且这些值被分配给了 u.s.ax[0]u.s.ax[1],所以 u.st.b 为零。这两个字符覆盖了 u.st.a 的最低有效端,使得上半部分和 u.st.b 默认设置为零。 - Borodin

2
一个联合体的大小是由组成联合体的最大元素的大小决定的。因此,在一个32位平台上,如果int类型占4字节,则您的联合类型大小为8字节。但是,联合体的第一个成员变量s只占用了2个字节,因此与st.a的前2个字节重叠。由于您使用的是小端系统,这意味着我们重叠了st.a的两个低序字节。因此,当您像使用{12, 1}这样的值初始化联合体时,您仅初始化了st.a的两个低序字节中的值...这使得st.b的值初始化为0。因此,当您尝试打印包含联合体的两个int成员而不是char成员的结构体时,您得到了128和0的结果。

输出是 2680。为什么联合体 s 的第一个成员占用了 2 个字节?它不应该占用 4 个字节吗?它有两个大小为 2 的字符数组。所以它应该是 2*2 = 4。如果我错了,请纠正我。 - h4ck3d

1

它可能将 { 12, 1 } 分配给 s.ax 中的前两个字符。

因此,在32位整数中,它是1 * 256 + 12 = 268。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接