联合体内部的结构体

14
int main()
{
  union {
    struct {
      char c[2];
      char ch[2]; 
    } s;
    struct {
       int i;
       int j; 
    } st;
  } u = { (12, 1), (15, 1) };

  printf("%d %d ", u.st.i, u.st.j);
}

为什么上面会输出“257 0”?

使用 {}() 有什么区别?

4个回答

14

{} 表示初始化一个子对象。

() 是用于对表达式进行分组的运算符,例如 (1+3)*2。你混淆了逗号运算符,逗号运算符会丢弃其左操作数并返回右操作数。 (12,1) 等同于 1

初始化一个 union 总是设置它的第一个成员并忽略其他成员。这是因为一次只能有一个成员存储值。

将标量值用于数组子对象的初始化,例如将 1 传递给 c[2] 进行初始化,会自动跳转到数组中。这被称为花括号省略。下一个 1 将初始化数组的第二个成员。

你将 1 分配给 c[2] 中的每个字符,然后将结果字节串作为小端 int 读回。数组 ch[2] 没有被显式初始化,在 C++ 中它将被设置为零,但我不确定在 C 中是否也是如此。

初始化程序 {{12, 1}, {15, 1}} 无法工作,因为花括号省略将第一个 } 解释为关闭整个 union

初始化程序 {{{12, 1}, {15, 1}}} 将避免花括号省略并设置两个数组。 {12, 1, 15, 1} 应该也会达到同样的效果。

请注意,标量值和字节串之间的转换是实现定义的;特别是它取决于大小端和 int 的大小。


1
+1,但是这个{{12, 1},{15, 1}}在使用gcc -std=c99编译时会出现错误:初始化器末尾有额外的花括号组。 - iabdalkader
使用 u = { {12, 1}, {15, 1} }; 报错如下。错误 C2078:初始化器过多。 - user1915016
@user1915016 抱歉,我没有尝试过。已更新答案。 - Potatoswatter

3

两个元组 (12, 1)(15, 1) 奇怪的是都简化为了 1。这是因为,正如 Omkant 所说,您正在使用逗号运算符,它执行它分隔的每个表达式,但返回最后一个表达式的值。实际上,维基百科条目 很好地解释了这一点。

结果是,u.s.c[0] 被填充了第一个 1,而 u.s.c[1] 被填充了第二个 1。由于联合将 int u.st.i 叠加在 u.c[2]u.ch[2] 上(假设 8 位字符和 32 位的 int),并且架构是小端的(从您的结果中可以得知),因此 u.st.i 的最低字节中有一个 1,并且在其第二低字节中有一个 1,总共的值为 256*1 + 1 = 257

同时,没有值写入到 u.st.j 的内存中,因此第二个输出为 0。


0
what the difference will it create if i use {} instead of ().

如果您使用的是(),那么,将是逗号运算符,并且分配的值将是括号内最右边的值。
但在{}的情况下,,逗号分隔符,每个单独的值都分配给相应的成员。但在这种情况下,它不会编译并会抛出错误:初始化器末尾有多余的花括号组。

0

看起来你误解了联合的作用,联合中的对象共享它们的内存,因此你只能初始化其中一个对象而不能同时初始化两个对象。换句话说,底层内存是相同的。正如其他答案中提到的,初始化联合总是设置其第一个成员,如果你想初始化第二个成员,可以使用C99指定的初始化器:

u = {.st={15, 1} };

而要初始化第一个结构中的数组:

u = { .s={{12, 1}, {15, 1}} };

或者对于第一个结构体,不使用.s

u = { {{12, 1}, {15, 1}} };

意图似乎是:初始化2+2个字符,而不是初始化两个结构体。恰好,结构体和数组初始化列表都用{}写。 - Lundin
@Lundin 但是 printf 打印的是第二个结构体,因此我的答案是正确的。 - iabdalkader
@Lundin 也为第一个添加了一个初始化器。 - iabdalkader

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