这个问题背后的想法是理解使用联合的更深层次的概念,并以不同的方式使用它以节省记忆.我的问题是 - 我的问题是 -
让我们说有一个结构
struct strt
{
float f;
char c;
int a;
}
Run Code Online (Sandbox Code Playgroud)
并且在结合中表示相同的结构
union unin
{
float f;
char c;
int a;
}
Run Code Online (Sandbox Code Playgroud)
如果我一个接一个地为结构成员分配值然后打印它们,它就会打印出来.但是如果没有发生联盟,一些覆盖工作正在进行中.
所以我需要找到一个方法,它可以存储f,c,a的值使用union,然后我可以打印相同的.(应用任何操作或任何东西..)但我正在寻找这种技术..那里有人可以指导我或给我任何想法吗?
Sma*_*ery 51
如果你要看一个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
Run Code Online (Sandbox Code Playgroud)
因此,当您更改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
Run Code Online (Sandbox Code Playgroud)
所以现在,当我改变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 - 它不节省空间 - 只是结构和联合不是同一个东西.
gre*_*ade 39
我想你误解了一个目的union.
甲union,顾名思义,定义了一个结构的所有成员占据相同的内存空间.而将struct其每个成员放置在单独的连续区域中的单独存储器中.
与你的工会,当你写:
union foo;
foo.c = 3;
Run Code Online (Sandbox Code Playgroud)
然后foo.a,foo.f两者都会改变.这是因为.a,.c并且.f存储在相同的存储位置.因此,联合的每个成员都是同一记忆的不同"视图".a不会发生这种情况,struct因为所有成员都是不同的并且彼此分开.
没有办法绕过这种行为,因为它是故意的.
Eur*_*lli 12
我想你是对工会的误解.
使用工会背后的想法是节省内存......
是的,这是一个原因
...并获得相当于结构的结果......
没有
它并不等同.它们在源代码中看起来很相似,但它完全不同.像苹果和飞机.
联合是一个非常非常低级的构造,它允许您看到一块内存,就像存储它的任何"成员"一样,但您一次只能使用一个.即使使用"成员"一词也极具误导性.它们应该被称为"观点"或其他东西,而不是成员.
当你写:
union ABCunion
{
int a;
double b;
char c;
} myAbc;
Run Code Online (Sandbox Code Playgroud)
你说的是:"把一块足够大的内存放到一个int,一个char和一个double中的最大内存,然后把它称为myAbc.
在那段记忆,现在你可以存储任何一个int,或双,或一个字符.如果存储一个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;
};
Run Code Online (Sandbox Code Playgroud)
例如,VB6的"变体"是Unions,与上面的不同(但更复杂).
b)拆分表示当你需要能够将变量看作"整体"或部分组合时,这有时很有用.用一个例子来解释更容易:
union DOUBLEBYTE
{
struct
{
unsigned char a;
unsigned char b;
} bytes;
short Integer;
} myVar;
Run Code Online (Sandbox Code Playgroud)
这是一个带有一对字节的短"int""unioned".现在,您可以查看与short int(myVar.Integer)相同的值,或者您可以轻松地研究构成值的各个字节(myVar.bytes.a和myVar.bytes.b).
请注意,第二次使用不可移植(我很确定); 意味着它不能保证在不同的机器架构中工作; 但这种使用对于C设计的任务类型(OS实现)是绝对必要的.
联合包含一组互斥数据.
在您的特定示例中,您可以将float(f),char(c)或int(a)存储在union中.但是,只会为联合中的最大项分配内存.联合中的所有项目将共享相同的内存部分.换句话说,将一个值写入联合后跟另一个将导致第一个值被覆盖.
你需要回去问自己你在建模什么:
你真的希望f,c和a的值是互斥的(即一次只能存在一个值)吗?如果是这样,请考虑将联合与枚举值(存储在联合外部)一起使用,以指示联合中哪个成员在任何特定时间点是"活动"成员.这将允许您以更危险的代码为代价获得使用联合的内存优势(因为任何维护它的人都需要知道值是互斥的 - 即它确实是一个联合). 如果要创建许多这些联合,并且内存保护至关重要(例如,在嵌入式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;
}
}
Run Code Online (Sandbox Code Playgroud)