不幸的是,在线资源中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,则a
和b
都是正确的。所以我推断,如果联盟成员的格式相同,那么当你写任何一个成员时,其他成员 WILL 包含相同的值(因为它们的格式相同),你可以阅读在任何时候都没有任何问题。但如果它们都是不同的格式,那么最后写的那个只是有效值。我发现没有在线资源明确说明这一点。这个假设是否正确?
答案 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)
}