C / C ++中的联合大小

时间:2009-04-11 18:27:19

标签: c++ c sizeof unions

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

8 个答案:

答案 0 :(得分:56)

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

union {
  short x;
  int y;
  long long z;
}

以上union的实例始终至少需要long long才能存储。

旁注:正如Stefano所述,任何类型的实际空间(unionstructclass)都将取决于在其他问题上,例如编译器的对齐。我没有简单地通过这个,因为我只想告诉工会考虑最大的项目。 了解实际尺寸 取决于对齐非常重要。

答案 1 :(得分:28)

标准回答了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放入每个结构中以显示常见的初始序列): / p>

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;
};

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

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

union float_cast { unsigned short s[2]; float f; };

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

答案 2 :(得分:16)

这取决于编译器和选项。

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));

}

输出:

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));

}

此输出

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    

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

答案 3 :(得分:10)

联合没有活动数据类型的概念。你可以自由地阅读和写作工会的任何“成员”:这取决于你解释你得到的东西。

因此,union的大小始终是其最大数据类型的大小。

答案 4 :(得分:3)

大小至少是最大作曲类型的大小。没有“主动”类型的概念。

答案 5 :(得分:2)

您应该将union作为其中最大数据类型的容器以及转换的快捷方式。当您使用其中一个较小的成员时,未使用的空间仍然存在,但它只是保持未使用状态。

您经常会在Unix中看到这与ioctl()调用结合使用,所有ioctl()调用都将传递相同的结构,其中包含所有可能响应的并集。例如。 这个例子来自/usr/include/linux/if.h,这个结构在ioctl()中用于配置/查询以太网接口的状态,请求参数定义了union的哪个部分实际使用:

struct ifreq 
{
#define IFHWADDRLEN 6
    union
    {
        char    ifrn_name[IFNAMSIZ];        /* if name, e.g. "en0" */
    } ifr_ifrn;

    union {
        struct  sockaddr ifru_addr;
        struct  sockaddr ifru_dstaddr;
        struct  sockaddr ifru_broadaddr;
        struct  sockaddr ifru_netmask;
        struct  sockaddr ifru_hwaddr;
        short   ifru_flags;
        int ifru_ivalue;
        int ifru_mtu;
        struct  ifmap ifru_map;
        char    ifru_slave[IFNAMSIZ];   /* Just fits the size */
        char    ifru_newname[IFNAMSIZ];
        void *  ifru_data;
        struct  if_settings ifru_settings;
    } ifr_ifru;
};

答案 6 :(得分:0)

  1. 最大会员的人数。

  2. 这就是为什么联盟通常在一个结构中有意义,该结构有一个标志,指示哪个是“活跃”成员。

  3. 示例:

    struct ONE_OF_MANY {
        enum FLAG { FLAG_SHORT, FLAG_INT, FLAG_LONG_LONG } flag;
        union { short x; int y; long long z; };
    };
    

答案 7 :(得分:0)

  

C / C ++中联合的大小是多少?它是最大的尺寸   里面的数据类型?

,联盟的大小是其最大成员的大小。

例如:

#include<stdio.h>

union un
{
    char c;
    int i;
    float f;
    double d;
};

int main()
{
    union un u1;
    printf("sizeof union u1 : %ld\n",sizeof(u1));
    return 0;
}

输出

sizeof union u1 : 8
sizeof double d : 8

这里最大的成员是double。两者的大小都为8。所以,正如sizeof正确告诉你的那样,联合的大小确实是8

  

编译器如何计算如何移动堆栈指针(如果有)   union的较小数据类型是否处于活动状态?

它由编译器在内部处理。假设我们正在访问union的一个数据成员,那么我们无法访问其他数据成员,因为我们可以访问union的单个数据成员,因为每个数据成员共享相同的内存。通过使用联盟,我们可以节省大量有价值的空间。