1

C 标准规定只有联合的成员存储在同一地址,因此,我们一次只能访问一个成员。由于编译器为联合的成员覆盖存储,更改一个成员会更改任何值以前存储在任何其他成员中。因此,如果我们尝试访问以前存储的成员的值,则该值将毫无意义或未定义。现在这是我的问题:-

struct catalog_item
{
   int stock_number;
   double price;
   int item_type;
   union
     {
       struct
          {
            char title[TITLE_LEN+1];
            char author[AUTHOR_LEN+1];
            int num_pages;
          } book;
       struct
          {
            char design[DESIGN_LEN+1];
          } mug;
       struct
          {
            char design[DESIGN_LEN+1];
            int colors;
            int sizes;
          } shirt;
     } item;
} c;

现在,如果完成以下操作

strcpy(c.item.mug.design, "Butterfly");

那么以下两个具有相同的值

printf("%s",c.item.mug.design);          //1

printf("%s",c.item.shirt.design);        //2

为什么“2”的结果不是未定义或无意义的?

4

3 回答 3

1

这不是未定义的行为,而是实现定义的行为,如果我们查看C99 草案标准 脚注 82说:

如果用于访问联合对象内容的成员与上次用于在对象中存储值的成员不同,则该值的对象表示的适当部分将被重新解释为新类型中的对象表示在 6.2.6 中描述(有时称为“类型双关语”的过程)。这可能是一个陷阱表示。

实际上,这是支持的C,您应该阅读特定的编译器文档。例如,如果您检查手册中结构、联合、枚举和位字段实现定义的行为部分,它指向此处用于类型双关,我们可以在-fstrict-aliasing部分看到说:gcc

从不同的工会成员那里阅读而不是最近写入的成员(称为“类型双关语”)的做法很常见。即使使用 -fstrict-aliasing,也允许使用类型双关语,前提是通过联合类型访问内存。

但是有一些警告,你应该阅读严格别名以了解所有细节,这篇文章是更温和的介绍。

为了完整性部分3.4.1实现定义的行为定义为:

未指定的行为,其中每个实现都记录了如何做出选择

以及列出标准所涵盖的未指定行为的标准附件J.1 指定行为包括以下行:

存储到 (6.2.6.1) 中的最后一个成员以外的联合成员的值。

于 2013-09-02T17:07:30.990 回答
1

从根本上说,您需要以不同的方式考虑 C 中的数据存储。本质上,C 中的 union 表示它可以容纳其中的任何项目(为包括的任何类型分配足够的内存),但单个实例只能容纳 1。当您访问一个字段时,例如整数,您正在查看内存中的位置并将内存中的位(0 和 1)视为整数。字符串被写成一个字符数组。您可以将内存中的任何位置视为一个数字,并且会有一个(由之前发生的任何事情构成)。现在您看到的是,将此联合视为两个可能结构中的任何一个都包含字符串。这是因为字符串的位置在任一结构中的相同位置(在开头,偏移量 0),因此无论您解析到内存中的相同位置。

请注意,这并不能保证以这种方式排列,而只是由于编译器对结构的解释而发生排列。

于 2013-09-02T15:18:12.440 回答
0

联合中的三个结构使用相同的内存区域。其中两个结构中的“设计”字段恰好位于同一内存位置。因此,写一个也写另一个,实际地址是一样的。

于 2013-09-02T15:43:19.437 回答