C中联合的内存共享

memory sharing for unions in C

不幸的是,在线资源中对 Unions in C 的特定行为的描述(如果需要,我可以列出一些)从一个来源到另一个来源有很大差异,在某些情况下还不够。其中一项资源说,You can define a union with many members, but only one member can contain a value at any given time. 仅此而已。然后另一个资源说,in union, the only member whose value is currently stored will have the memory. 所以,现在如果我 运行 这个程序,

#include <stdio.h>

union item
{
 int a;
 float b;
 char ch;
};

int main( )
{
 union item it;
 it.a = 12;
 it.b = 20.2;
 it.ch='z';
 printf("%d\n",it.a);
 printf("%f\n",it.b);
 printf("%c\n",it.ch);
 return 0;
}

我的输出为:

1101109626
20.199940
z

在线网站指出 ab 都已损坏,尽管我在这里略有不同,因为 b 接近到 20.2。无论如何,现在如果我在开头写 char 然后写 a 和 b(仍然是相同的格式),我会看到 b 具有正确的值,但其他两个已损坏。但是,如果我将 b 声明为 int,则 ab 都是正确的。所以我推断,如果 union 的成员格式相同,那么当你写任何一个成员时,其他成员 WILL 包含相同的值(因为它们格式相同)您可以随时阅读,没有任何问题。但是如果都是不同的格式,那么最后写的就是有效值。我没有找到明确说明这一点的在线资源。这个假设正确吗?

But if they are all of different format, then the one who was written last is only the valid value.

你几乎是正确的。


当你写入联合的一个成员并读取另一个成员(不是最后写入的那个)时,行为是未指定的,可以是陷阱表示。

来自 C11 n1570 草案的一个脚注(参见 6.5.2.3 中的脚注 95):

If the member used to read 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’’). This might be a trap representation.

C union 的整体思想是为不同的类型共享相同的存储区域。如果 union 的所有成员都是同一类型,那么拥有 union 根本就没有意义,因为对于所有目的来说,它都等于该类型的单个实例。

联合可以帮助您实现type punning,即"raw"不同类型之间的转换,但该行为应被视为UB,并且依赖于平台和编译器。有时这种行为正是您想要的:例如您可能希望将 32 位 float 的本机表示转换为 32 位整数,或者将两个 32 位整数的结构视为与单个 64 位整数的联合以执行 64-位运算,仍然可以简单地访问高位和低位字。

一般来说,当您只需要在任何给定时刻存储特定类型的值时,您会希望使用它来保存 space。请记住,您也可以拥有 struct 的任意组合的并集,不仅是原始类型,而且它的内存 space 将得到有效利用; union 将具有最大结构的大小。

"You can define a union with many members, but only one member can contain a value at any given time." 是正确的说法。

联合的大小是其最大成员的大小(可能加上一些填充)。使用成员指示编译器使用该成员的类型。

 struct example {
      int what_is_in_it;
      union {
           int a;
           long b;
           float f;
      } u;
 } e;

 #define ITHASANINT     1
 #define ITHASALONG     2
 #define ITHASAFLOAT    3

 switch (e.what_is_in_it) {
      case ITHASANINT: printf("%d\n", e.u.a); break;    // compiler passes an int
      case ITHASALONG: printf("%ld\n", e.u.b); break;   // compiler passes a long
      case ITHASAFLOAT:printf("%f\n", e.u.f); break;    // compiler passes a float (promoted to double)
}

正如评论和其他答案所解释的那样,联合(和结构)的目的是允许复合变量类型,特别是在联合的情况下,在成员之间共享内存。任何时候只有一个成员拥有分配给联合的内存是有道理的。如果碰巧,在一个成员被赋值后,但另一个成员似乎保留了其先前赋值的值,这纯属偶然,应被视为未定义(或未指定)行为。简单来说,不要依赖它。

Web 参考有时可以提供额外的指示,但这里是 C 标准关于该主题的一些内容:

C99 6.2.5.20

A union type describes an overlapping nonempty set of member objects, each of which has an optionally specified name and possibly distinct type.

下面几行: C99 6.2.6.1.7

When a value is stored in a member of an object of union type, the bytes of the object representation that do not correspond to that member but do correspond to other members take unspecified values.