C中的联合与结构

这个问题背后的想法是理解使用联合的更深层次的概念,并以不同的方式使用它以节省记忆。我的问题是 – 我的问题是 –

让我们说有一个结构

struct strt { float f; char c; int a; } 

并且在联合中表示相同的结构

 union unin { float f; char c; int a; } 

如果我一个接一个地为结构成员分配值然后打印它们,它就会打印出来。 但是在联盟的情况下它不会发生,一些覆盖正在进行中。

所以我需要找到一个方法,它可以存储f,c,a的值使用union,然后我可以打印相同的。 (应用任何操作或任何东西..)但我正在寻找这种技术..任何人可以指导我或给我任何想法?

如果你要看一个struct如何存储它的值,它将是这样的:

 |0---1---2---3---|4---|5---6---7---8---| |ffffffffffffffff| | | <- f: Where your float is stored | |cccc| | <- c: Where your char is stored | | |aaaaaaaaaaaaaaaa| <- a: Where your int is stored 

因此,当您更改f的值时,实际上您正在更改字节0-3。 更改char时,实际上是在更改字节4.更改int时,实际上是在更改字节5-8。

如果你现在看一下union如何存储它的值,它将是这样的:

 |0---1---2---3---| |ffffffffffffffff| <- f: where your float is stored |cccc------------| <- c: where your char is stored |aaaaaaaaaaaaaaaa| <- a: where your int is stored 

所以现在,当我改变f的值时,我正在改变字节0-3。 由于c存储在字节0中,当你改变f时,你也改变了c和a! 当你改变c时,你正在改变f和a的一部分 - 当你改变a时,你正在改变c和f。 这就是你的“覆盖”正在发生的地方。 将3个值打包到一个内存地址时,根本不是“节省空间”; 您只是创建了3种不同的查看和更改相同数据的方式。 你在该联合中没有真正的int,float和char - 在物理层面,你只有32位,可以看作是int,float或char。 改变一个意味着改变其他人。 如果您不希望它们相互更改,请使用结构。

这就是为什么gcc告诉你你的结构长度为9个字节,而你的联合只有4个 - 它不能节省空间 - 它只是结构和联合不是同一个东西。

我想你误解了union的目的。

顾名思义,联合定义了一个结构,其中所有成员占用相同的内存空间 。 而struct将其每个成员放在单独的连续区域中的单独内存中。

与你的工会,当你写:

 union foo; foo.c = 3; 

然后foo.afoo.f都将被更改。 这是因为.a.c.f存储在同一个内存位置 。 因此,联合的每个成员都是同一记忆的不同“视图”。 struct不会发生这种情况,因为所有成员都是不同的并且彼此分开。

没有办法绕过这种行为, 因为它是故意的。

我想你是对工会的误解。

使用工会背后的想法是节省内存……

是的,这是一个原因

…并获得相当于结构的结果……

没有

它不等同。 它们在源代码中看起来很相似,但它完全不同。 像苹果和飞机。

联合是一个非常非常低级别的构造,它允许您看到一块内存,就像存储它的任何“成员”一样,但您一次只能使用一个 。 即使使用“成员”一词也极具误导性。 它们应该被称为“观点”或其他东西,而不是成员。

当你写:

 union ABCunion { int a; double b; char c; } myAbc; 

你说的是:“在一个int,一个char和一个double中占用一块足够大的内存,让我们称之为myAbc

在该内存中,现在可以存储int,double char。 如果存储一个int,然后存储一个double,则int将永远消失。

那有什么意义呢?

联盟有两个主要用途。

a)歧视存储

这就是我们上面所做的。 我选择了一段记忆,并根据具体情况赋予它不同的含义。 有时上下文是显式的(你保留一些变量来表示你存储的变量的“种类”),有时它可以是隐式的(根据代码部分,你可以知道哪一个必须使用)。 无论哪种方式,代码都需要能够弄明白,否则你将无法对变量做任何合理的事情。

一个典型的(显式)示例是:

 struct MyVariantType { int typeIndicator ; // type=1 -> It's an int, // type=2 -> It's a double, // type=3 -> It's a char ABCunion body; }; 

例如,VB6的“变体”是Unions,与上述不同(但更复杂)。

b)拆分表示当您需要能够将变量视为“整体”或部件组合时,这有时很有用。 用一个例子来解释更容易:

 union DOUBLEBYTE { struct { unsigned char a; unsigned char b; } bytes; short Integer; } myVar; 

这是一个短的int“联合”,带有一对字节。 现在,您可以查看与short int(myVar.Integer)相同的值,或者您可以轻松地研究构成值的各个字节(myVar.bytes.a和myVar.bytes.b)。

请注意,第二次使用不可移植(我很确定); 意味着它不能保证在不同的机器架构中工作; 但这种使用对于C设计的任务类型(OS实现)是绝对必要的。

联合包含一组互斥的数据。

在您的特定示例中,您可以将float( f ),char( c )或int( a )存储在union中。 但是,只会为联合中的最大项目分配内存。 联合中的所有项目将共享相同的内存部分 。 换句话说,将一个值写入联合后跟另一个值将导致第一个值被覆盖。

你需要回去问问自己你在建模什么

  • 你真的希望fca的值是互斥的(即一次只能存在一个值)吗? 如果是这样,请考虑将联合与枚举值(存储在联合外部)一起使用,以指示联合中哪个成员在任何特定时间点是“活动”成员。 这将允许您以更危险的代码为代价获得使用联合的内存优势(因为任何维护它的人都需要知道值是互斥的 – 即它确实是一个联合)。 如果要创建许多这些联合,并且内存保护至关重要(例如,在嵌入式CPU上),则只考虑此选项。 您甚至可能不会最终节省内存,因为您需要在堆栈上创建枚举变量,这也将占用内存。

  • 您是否希望这些值同时处于活动状态而不会相互干扰? 如果是这样,您将需要使用结构(如您在第一个示例中所示)。 这将使用更多内存 – 当您实例化一个结构时,分配的内存是所有成员的总和(加上一些填充到最近的单词边界)。 除非记忆保存至关重要(参见前面的例子),否则我会赞成这种方法。

编辑:

(非常简单)如何将enums与union结合使用的示例:

 typedef union { float f; char c; int a; } floatCharIntUnion; typedef enum { usingFloat, usingChar, usingInt } unionSelection; int main() { floatCharIntUnion myUnion; unionSelection selection; myUnion.f = 3.1415; selection = usingFloat; processUnion(&myUnion, selection); myUnion.c = 'a'; selection = usingChar; processUnion(&myUnion, selection); myUnion.a = 22; selection = usingInt; processUnion(&myUnion, selection); } void processUnion(floatCharIntUnion* myUnion, unionSelection selection) { switch (selection) { case usingFloat: // Process myUnion->f break; case usingChar: // Process myUnion->c break; case usingInt: // Process myUnion->a break; } } 

这是使用联合根据外部标记存储数据的典型示例。

int,float和char *都占据了union中的相同位置,它们不是连续的,所以,如果你需要将它们全部存储起来,它就是你正在寻找的结构,而不是联合。

结构是联合中最大的东西的大小加上类型的大小,因为它在联合之外。

 #define TYP_INT 0 #define TYP_FLT 1 #define TYP_STR 2 typedef struct { int type; union data { int a; float b; char *c; } } tMyType; static void printMyType (tMyType * x) { if (x.type == TYP_INT) { printf ("%d\n", x.data.a; return; } if (x.type == TYP_FLT) { printf ("%f\n", x.data.b; return; } if (x.type == TYP_STR) { printf ("%s\n", x.data.c; return; } } 

printMyType函数将正确检测结构中存储的内容(除非您撒谎)并打印出相关值。

当您填充其中一个时,您必须:

 x.type = TYP_INT; x.data.a = 7; 

要么

 x.type = TYP_STR; x.data.c = "Hello"; 

给定的x一次只能是一件事。

有人试图尝试:

 x.type = TYP_STR; x.data.a = 7; 

他们要求麻烦。

当在任何给定的时间点仅将下面的一个存储在实例中时,通常使用联合。 即你可以在任何时刻存储一个浮点数,一个char或一个int。 这是为了节省内存 – 当你打算用它来存储一个char时,不为float和int分配额外的/不同的内存。 分配的内存量=联合中的最大类型。

 union unin { float f; char c; int a; } 

union的另一个用途是当你想要存储具有部分的东西时,让你可能想要将寄存器建模为包含高字节,低字节和复合值的联合。 因此,您可以将合成值存储到联合中,并使用成员通过其他成员获取碎片。