C中联合的内存共享

时间:2016-03-22 13:11:56

标签: c unions

不幸的是,在线资源中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

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

4 个答案:

答案 0 :(得分:4)

  

但是,如果它们都是不同的格式,那么就是那个写的人   last只是有效值。

你几乎是正确的。

当您编写union的一个成员并读取另一个成员(最后一个未写入的成员)时,行为未指定,可以是陷阱表示。

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

  

如果用于读取union对象内容的成员不是   与上次用于在对象中存储值的成员相同,   值的对象表示的适当部分是   如上所述,重新解释为新类型中的对象表示   在6.2.6(一个有时被称为''type punning''的过程)。这可能是   陷阱表示。

答案 1 :(得分:3)

C union的整个想法是为不同类型共享相同的存储区域。如果联盟的所有成员都属于同一类型,那么根本就没有任何联盟是没有意义的,因为它对于所有目的而言都等于该类型的单个实例。

工会可以帮助您实现type punning,即&#34; raw&#34;不同类型之间的转换,但行为应该被视为UB并且依赖于平台和编译器。有时这种行为正是您想要的:例如您可能希望将32位float的本机表示形式转换为32位整数,或将两个32位整数的结构视为具有单个64位整数的并集以执行64-有点算术,仍然可以轻松访问高低字。

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

答案 2 :(得分:2)

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

Web引用有时可以提供额外的权限,但以下是C标准对该主题的一些说法:

C99 6.2.5.20

  

联合类型描述了一组重叠的成员对象,   每个都有一个可选的指定名称,可能是不同的   类型。

几行: C99 6.2.6.1.7

  

当一个值存储在union类型的对象的成员中时,   对象表示的字节与该对应的字节不对应   成员,但确实对应其他成员采取未指定的值。

答案 3 :(得分:0)

“您可以定义具有许多成员的联合,但在任何给定时间只能有一个成员包含值。”是核心声明。

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

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