简体   繁体   English

C-结构和联合

[英]C- Structures and Unions

C standard states that only the members of a union are stored at the same address and ,because of which, we can access only one member at a time.Since the compiler overlays storage for the members of a union, changing one member alters any value previously stored in any of the other members.So if we try to access the value of a member stored previously, the value will be meaningless or undefined.Now here is my question:- 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;

Now if the following is done 现在,如果完成以下操作

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

then both of the following have the same value 那么以下两个都具有相同的值

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

and

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

Why is the result of "2" is not undefined or meaningless? 为什么“ 2”的结果不是未定义或没有意义?

Fundamentally, you need to think about data storage in C differently. 从根本上讲,您需要以不同的方式考虑C语言中的数据存储。 In essence, a union in C says that it can hold any of the items in it (enough memory is allocated for any type included), but that a single instance will only hold 1. When you access a field, such as an integer, you're looking at the place in memory and treating the bits (0s and 1s) in memory there as an integer. 本质上,C语言中的并集表示它可以容纳其中的任何项(为包括的任何类型分配足够的内存),但是单个实例将仅容纳1。当您访问字段(例如整数)时,您正在查看内存中的位置,并将内存中的位(0和1s)视为整数。 Strings are written as an array of characters. 字符串被写为字符数组。 You can look at any place in memory as a number and there will be one (constructed of whatever happen to be there before). 您可以将内存中的任何位置看成一个数字,并且会有一个(由之前发生的任何事情构成)。 Now what you are seeing is that treating this union as either of two possible structures both contain the string. 现在,您将看到将此联合视为两个可能的结构之一都包含字符串。 This is because the location of the string is in the same place in either struct (at the beginning, offset 0), so with either you're resolving to the same location in memory. 这是因为字符串的位置在任何一个结构中的相同位置(起始处,偏移量为0),因此无论使用哪种方法,您都将解析到内存中的相同位置。

Note that this is not guaranteed to line up this way, but only happens to line up because of your compilers interpretation of the structures. 请注意,这不能保证以这种方式排列,而是仅由于编译器对结构的解释而发生排列。

This is not undefined behavior but it is implementation defined behavior, if we look at the C99 draft standard footnote 82 says: 如果我们看一下C99草案标准 脚注82中的内容 ,这不是未定义的行为,而是实现定义的行为。

If the member used to access the contents of a union object is not the same as the member last used to store a value in the object , the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called "type punning"). 如果用于访问联合对象内容的成员与上次用于在对象中存储值的成员不同,则该值的对象表示形式的适当部分将重新解释为新类型的对象表示形式,如下所示:在6.2.6中描述(有时称为“类型校正”的过程)。 This might be a trap representation. 这可能是陷阱表示。

In practice this is supported in C and you should read the specific compilers documentation. 实际上,这在C受支持,您应该阅读特定的编译器文档。 For example if you examine the Implementation-defined behavior section for Structures, unions, enumerations, and bit-fields in the gcc manual it points to here for Type-punning and we can see under the -fstrict-aliasing section is says: 例如,如果您在gcc手册中检查“ 结构,联合,枚举和位字段实现定义的行为”部分,则它指向此处进行“类型调整” ,我们可以在-fstrict-aliasing部分看到:

The practice of reading from a different union member than the one most recently written to (called “type-punning”) is common. 从与最近写过的工会成员不同的工会成员那里进行阅读的做法很常见(称为“类型操纵”)。 Even with -fstrict-aliasing, type-punning is allowed, provided the memory is accessed through the union type. 即使使用-fstrict-aliasing,只要通过联合类型访问内存,也可以进行类型修剪。

but there are caveats and you should read up on strict aliasing to understand all the details, this article is gentler introduction. 但是有一些警告,您应该阅读严格的别名以了解所有详细信息, 本文是较温和的介绍。

For completeness section 3.4.1 defines implementation-defined behavior as: 为了完整性,第3.4.1节将实现定义的行为定义为:

unspecified behavior where each implementation documents how the choice is made 未指明的行为,其中每个实现都记录了如何做出选择

and the standard annex J.1 Unspecified behavior which lists the unspecified behavior covered by the standard includes this line: 标准附件J.1 未指定行为列出了该标准涵盖的未指定行为,包括以下行:

The value of a union member other than the last one stored into (6.2.6.1). 除存储在(6.2.6.1)中的最后一个成员以外的工会成员的值。

Your three structs in the union use the same memory area. 联合中的三个结构使用相同的存储区。 The "design" field in two of those structs happen to fall in the same memory location. 这些结构中的两个结构中的“设计”字段恰好位于同一存储位置。 Thus, writing one also writes the other, the actual address is the same. 因此,写一个也写另一个,实际地址是相同的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM