C/C++中的联合大小

Nav*_*een 41 c c++ sizeof unions

C/C++中联合的大小是多少?它是最大数据类型的sizeof吗?如果是这样,如果联合的较小数据类型之一处于活动状态,编译器如何计算如何移动堆栈指针?

Meh*_*ari 58

A union总是占用最大成员的空间.目前使用的无关紧要.

union {
  short x;
  int y;
  long long z;
}
Run Code Online (Sandbox Code Playgroud)

上述实例union总是至少long long需要存储空间.

边注:正如指出的斯蒂法诺,实际空间的任何类型(union,struct,class)将采取不依赖于其他问题,如编译器对齐.我没有简单地通过这个,因为我只想告诉工会考虑最大的项目.重要的是要知道实际尺寸确实取决于对齐.


Joh*_*itb 30

标准回答了C++标准第9.5节或C99标准第6.5.2.3节(或C11标准第6段)中的所有问题:

在联合中,最多一个数据成员可以在任何时间处于活动状态,也就是说,最多一个数据成员的值可以随时存储在并集中.[注意:为了简化联合的使用,我们做了一个特别的保证:如果一个POD-union包含几个共享一个公共初始序列的POD结构(9.2),并且这个POD-union类型的一个对象包含一个在POD结构中,允许检查任何POD结构成员的共同初始序列; 见9.2.] union的大小足以包含其最大的数据成员.每个数据成员都被分配,就好像它是结构的唯一成员一样.

这意味着每个成员共享相同的内存区域.这里活跃在最一个成员,但你不能找出哪一个.您必须在其他地方存储有关当前活动成员的信息.除了union之外还存储这样的标志(例如,有一个结构,其中一个整数作为type-flag,union作为数据存储)将给你一个所谓的"区别联合":一个知道什么类型的联合它目前是"活跃的".

一个常见的用法是在词法分析器中,你可以使用不同的标记,但是根据标记,你有不同的信息存储(放入line每个结构以显示常见的初始序列):

struct tokeni {
    int token; /* type tag */
    union {
        struct { int line; } noVal;
        struct { int line; int val; } intVal;
        struct { int line; struct string val; } stringVal;
    } data;
};
Run Code Online (Sandbox Code Playgroud)

标准允许您访问line每个成员,因为这是每个成员的共同初始序列.

存在编译器扩展,允许访问所有成员而忽略当前存储其值的那个成员.这允许在每个成员之间有效地重新解释具有不同类型的存储比特.例如,以下内容可用于将float变量解析为2个unsigned short:

union float_cast { unsigned short s[2]; float f; };
Run Code Online (Sandbox Code Playgroud)

在编写低级代码时,这非常方便.如果编译器不支持该扩展,但无论如何都要支持,那么编写未定义结果的代码.因此,如果您使用该技巧,请确保您的编译器支持它.

  • 一个糟糕的标准语言恕我直言的可怕例子 - 事实上,关于工会的整个部分似乎有点吝啬.为什么要引入"主动"的概念? (3认同)
  • 海湾合作委员会至少明确支持工会成员的交叉阅读.如果成员按照3.10/15以某种方式相关或者是布局兼容的,我很确定你仍然可以阅读其他成员,即使它不是"活跃的"成员. (2认同)

Ste*_*ini 17

这取决于编译器和选项.

int main() {
  union {
    char all[13];
    int foo;
  } record;

printf("%d\n",sizeof(record.all));
printf("%d\n",sizeof(record.foo));
printf("%d\n",sizeof(record));

}
Run Code Online (Sandbox Code Playgroud)

这输出:

13 4 16

如果我没记错的话,它取决于编译器放入分配空间的对齐方式.因此,除非您使用某些特殊选项,否则编译器会将填充放入您的联合空间.

编辑:使用gcc你需要使用pragma指令

int main() {
#pragma pack(push, 1)
      union {
           char all[13];
           int foo;
      } record;
#pragma pack(pop)

      printf("%d\n",sizeof(record.all));
      printf("%d\n",sizeof(record.foo));
      printf("%d\n",sizeof(record));

}
Run Code Online (Sandbox Code Playgroud)

这个输出

13 4 13

您也可以从反汇编中看到它(为了清楚起见,删除了一些printf)

  0x00001fd2 <main+0>:    push   %ebp             |  0x00001fd2 <main+0>:    push   %ebp
  0x00001fd3 <main+1>:    mov    %esp,%ebp        |  0x00001fd3 <main+1>:    mov    %esp,%ebp
  0x00001fd5 <main+3>:    push   %ebx             |  0x00001fd5 <main+3>:    push   %ebx
  0x00001fd6 <main+4>:    sub    $0x24,%esp       |  0x00001fd6 <main+4>:    sub    $0x24,%esp
  0x00001fd9 <main+7>:    call   0x1fde <main+12> |  0x00001fd9 <main+7>:    call   0x1fde <main+12>
  0x00001fde <main+12>:   pop    %ebx             |  0x00001fde <main+12>:   pop    %ebx
  0x00001fdf <main+13>:   movl   $0xd,0x4(%esp)   |  0x00001fdf <main+13>:   movl   $0x10,0x4(%esp)                                         
  0x00001fe7 <main+21>:   lea    0x1d(%ebx),%eax  |  0x00001fe7 <main+21>:   lea    0x1d(%ebx),%eax
  0x00001fed <main+27>:   mov    %eax,(%esp)      |  0x00001fed <main+27>:   mov    %eax,(%esp)
  0x00001ff0 <main+30>:   call  0x3005 <printf>   |  0x00001ff0 <main+30>:   call   0x3005 <printf>
  0x00001ff5 <main+35>:   add    $0x24,%esp       |  0x00001ff5 <main+35>:   add    $0x24,%esp
  0x00001ff8 <main+38>:   pop    %ebx             |  0x00001ff8 <main+38>:   pop    %ebx
  0x00001ff9 <main+39>:   leave                   |  0x00001ff9 <main+39>:   leave
  0x00001ffa <main+40>:   ret                     |  0x00001ffa <main+40>:   ret    
Run Code Online (Sandbox Code Playgroud)

唯一的区别在于main + 13,编译器在堆栈0xd而不是0x10上分配

  • 是的,我想我们都应该说"在_least_和最大的包含类型一样大". (3认同)

mou*_*iel 11

联合没有活动数据类型的概念.您可以自由地阅读和撰写工会的任何"成员":这取决于您解释所获得的内容.

因此,union的size始终是其最大数据类型的size.

  • 你说"没有关于联合的活动数​​据类型的概念".你错了; 拥有它.声称你的意思与你所写的完全不同,试图避免出错是不可能的."没有什么能阻止编码人员写一个浮点数并读取一个int(并得到垃圾)." - 当然没有什么能阻止它...... C标准不会*阻止*任何东西; 它只告诉你是否定义了这种行为 - 事实并非如此.正如反复提到的,UB包括任何东西,甚至包括核武器引爆.对于某些人来说,这阻止了他们对UB进行编码. (3认同)
  • 你当然是错的......标准的语言明确指的是活动数据类型.但是,sizeof是编译时操作,因此当然不依赖于活动数据类型. (2认同)
  • @JimBalter - 你对标准是正确的。我的意思是,在 C 中,您不能查询联合的 _active 数据类型_。没有什么可以阻止编码器写入浮点数并读取 int(并获取垃圾)。 (2认同)