代码之家  ›  专栏  ›  技术社区  ›  Naveen

C/C++中联盟的大小

  •  37
  • Naveen  · 技术社区  · 15 年前

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

    8 回复  |  直到 8 年前
        1
  •  28
  •   hmijail    8 年前

    该标准回答C99标准第9.5节中的所有问题,或C99标准(或C11标准的第6段)第5.5.2.3段5的所有问题:

    在联合中,任何时候最多可以有一个数据成员处于活动状态,也就是说,任何时候最多可以将一个数据成员的值存储在联合中。[注:为了简化联合的使用,作出了一项特别保证:如果一个pod union包含多个共享公共初始序列(9.2)的pod结构,并且如果此pod union类型的对象包含一个pod结构,则允许检查任何pod结构成员的公共初始序列;见9.2。]联合的大小足以包含其最大的数据成员。每个数据成员的分配方式就像它是结构的唯一成员一样。

    这意味着每个成员共享相同的内存区域。那里 最多有一个成员处于活动状态,但您无法确定是哪个成员。您必须将有关当前活动成员的信息存储在其他地方。除了联合之外,存储这样一个标志(例如,有一个整数作为类型标志的结构,一个联合作为数据存储)将为您提供一个所谓的“区分联合”:一个知道它中当前是什么类型的“活动联合”。

    一个常见的用法是在lexers中,您可以拥有不同的令牌,但是根据令牌,您有不同的信息要存储(放置 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;
    };
    

    标准允许您访问 线 对于每个成员,因为这是每个成员的公共初始序列。

    存在允许访问所有成员的编译器扩展,而不管当前存储哪个成员的值。它允许对每个成员中不同类型的存储位进行有效的重新解释。例如,以下内容可用于将浮点变量分解为2个无符号短路:

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

    这在编写低级代码时非常方便。如果编译器不支持该扩展,但您仍然支持它,那么您将编写结果未定义的代码。因此,如果您使用这个技巧,一定要确保您的编译器支持它。

        2
  •  53
  •   Community CDub    7 年前

    union 总是占用和最大成员一样多的空间。不管目前使用的是什么。

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

    上面的一个例子 联盟 总是至少需要一个 long long 用于存储。

    边注 正如所指出的 Stefano ,实际空间任意类型( 联盟 , struct , class )will take依赖于其他问题,如编译器的对齐。我不是为了简单起见才这么做的,因为我只是想告诉大家,工会要考虑到最大的问题。 重要的是要知道实际尺寸 取决于对齐方式 .

        3
  •  15
  •   Stefano Borini    15 年前

    它取决于编译器和选项。

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

    此输出:

    十三 四 十六

    如果我记得正确,这取决于编译器放入分配空间的对齐方式。所以,除非您使用一些特殊的选项,否则编译器会将填充放到联合空间中。

    编辑:使用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));
    
    }
    

    这个输出

    十三 四 十三

    您也可以从拆解中看到它(为了清晰起见,删除了一些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上分配。

        4
  •  10
  •   mouviciel    15 年前

    联合没有活动数据类型的概念。你可以自由地读写工会的任何“成员”:这由你来解释你所得到的。

    因此,联合的sizeof总是其最大数据类型的sizeof。

        5
  •  3
  •   anon    15 年前

    大小至少为最大的撰写类型。没有“活动”类型的概念。

        6
  •  2
  •   amo-ej1    15 年前

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

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

    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;
    };
    
        7
  •  0
  •   Mehrdad Afshari    15 年前
    1. 最大成员的大小。

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

    例子:

    struct ONE_OF_MANY {
        enum FLAG { FLAG_SHORT, FLAG_INT, FLAG_LONG_LONG } flag;
        union { short x; int y; long long z; };
    };
    
        8
  •  0
  •   msc    8 年前

    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 没错,工会的规模 .

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

    它由编译器内部处理。假设我们正在访问union的一个数据成员,那么我们就不能访问其他数据成员,因为我们可以访问union的单个数据成员,因为每个数据成员共享相同的内存。通过使用联合,我们可以节省很多宝贵的空间。