是否有任何好的例子来区分a struct
和a union
?基本上我知道它struct
使用其成员的所有内存并union
使用最大的成员内存空间.是否有其他操作系统级别的差异?
使用union,你只应该使用其中一个元素,因为它们都存储在同一个地方.当您想要存储可能是多种类型之一的东西时,这会很有用.另一方面,结构体的每个元素都有一个单独的存储位置,它们都可以一次使用.
为了给出它们的使用的具体示例,我不久前正在研究Scheme解释器,我基本上将Scheme数据类型覆盖到C数据类型上.这涉及在结构中存储指示值类型的枚举和存储该值的联合.
union foo { int a; // can't use both a and b at once char b; } foo; struct bar { int a; // can use both a and b simultaneously char b; } bar; union foo x; x.a = 3; // OK x.b = 'c'; // NO! this affects the value of x.a! struct bar y; y.a = 3; // OK y.b = 'c'; // OK
编辑:如果你想知道什么设置xb到'c'改变了xa的值,从技术上讲它是未定义的.在大多数现代机器上,char是1个字节,int是4个字节,因此给xb赋值'c'也给xa的第一个字节赋予相同的值:
union foo x; x.a = 3; x.b = 'c'; printf("%i, %i\n", x.a, x.b);
版画
99, 99
为什么两个值相同?因为int 3的最后3个字节都是零,所以它也被读为99.如果我们为xa输入一个更大的数字,你会发现情况并非总是这样:
union foo x; x.a = 387439; x.b = 'c'; printf("%i, %i\n", x.a, x.b);
版画
387427, 99
为了更仔细地查看实际的内存值,让我们设置并打印出十六进制的值:
union foo x; x.a = 0xDEADBEEF; x.b = 0x22; printf("%x, %x\n", x.a, x.b);
版画
deadbe22, 22
你可以清楚地看到0x22覆盖了0xEF的位置.
但
在C中,未定义 int中的字节顺序.这个程序在我的Mac上用0x22覆盖了0xEF,但是还有其他平台会覆盖0xDE,因为构成int的字节顺序是相反的.因此,在编写程序时,不应该依赖于在联合中覆盖特定数据的行为,因为它不可移植.
有关字节顺序的更多读数,请查看字节顺序.
这是简短的回答:struct是一个记录结构:struct中的每个元素都分配了新的空间.所以,像一个结构
struct foobarbazquux_t { int foo; long bar; double baz; long double quux; }
(sizeof(int)+sizeof(long)+sizeof(double)+sizeof(long double))
为每个实例在内存中至少分配字节数.("至少"因为体系结构对齐约束可能会强制编译器填充结构.)
另一方面,
union foobarbazquux_u { int foo; long bar; double baz; long double quux; }
分配一块内存并给它四个别名.因此sizeof(union foobarbazquux_u) ? max((sizeof(int),sizeof(long),sizeof(double),sizeof(long double))
,再次有可能添加一些对齐.
有没有什么好的例子来区分'struct'和'union'?
一种虚构的通信协议
struct packetheader { int sourceaddress; int destaddress; int messagetype; union request { char fourcc[4]; int requestnumber; }; };
在这个虚构协议中,已经指定,基于"消息类型",标题中的以下位置将是请求号或四字符代码,但不是两者.简而言之,工会允许相同的存储位置代表多种数据类型,在此保证您只想在任何时候存储一种类型的数据.
工会主要是基于C作为系统编程语言的传统的低级细节,其中"重叠"存储位置有时以这种方式使用.有时,您可以使用联合来节省内存,其中您拥有一个数据结构,其中一次只能保存多种类型中的一种.
一般来说,操作系统并不关心或了解结构和联合 - 它们都只是简单的内存块.结构是一个存储器块,用于存储多个数据对象,这些对象不会重叠.union是一个存储多个数据对象的内存块,但只有最大的存储块,因此任何时候都只能存储一个数据对象.
因为你已经在你的问题的状态,之间的主要区别union
和struct
是union
成员相互叠加的内存,使得工会的sizeof是一个,而struct
成员(其间有可选填充)奠定了一个后对方.联合也足够大以包含其所有成员,并且具有适合其所有成员的对齐.因此,假设int
只能存储在2字节地址并且是2字节宽,而long只能存储在4字节地址并且长度为4字节.以下联盟
union test { int a; long b; };
可以有一个sizeof
4,对齐要求为4. union和struct都可以在结尾处有填充,但不能在它们的开头.写入结构只会更改写入的成员的值.写入联合成员将使所有其他成员的值无效.如果您之前没有写过它们,则无法访问它们,否则行为未定义.GCC提供了一个扩展,您可以实际读取工会成员,即使您最近没有写过它们.对于操作系统,用户程序是写入联合还是结构无关紧要.这实际上只是编译器的一个问题.
union和struct的另一个重要属性是,它们允许指向它们的指针指向其任何成员的类型.所以以下是有效的:
struct test { int a; double b; } * some_test_pointer;
some_test_pointer可以指向int*
或double*
.如果你将类型的地址转换test
为int*
,它实际上将指向它的第一个成员a
.工会也是如此.因此,因为union将始终具有正确的对齐方式,所以可以使用union来指向某些类型的有效:
union a { int a; double b; };
该联合实际上可以指向一个int和一个double:
union a * v = (union a*)some_int_pointer; *some_int_pointer = 5; v->a = 10; return *some_int_pointer;
实际上是有效的,如C99标准所述:
对象的存储值只能由具有以下类型之一的左值表达式访问:
与对象的有效类型兼容的类型
...
聚合或联合类型,包括其成员中的上述类型之一
编译器不会优化,v->a = 10;
因为它可能影响值*some_int_pointer
(并且函数将返回10
而不是5
).
A union
在几个场景中很有用.
union
可以成为非常低级别操作的工具,例如为内核编写设备驱动程序.
这方面的一个例子是一个解剖float
通过使用数目union
的struct
与位域和一个float
.我保存了一个数字float
,后来我可以访问float
通过它的特定部分struct
.该示例显示了如何union
使用不同的角度来查看数据.
#includeunion foo { struct float_guts { unsigned int fraction : 23; unsigned int exponent : 8; unsigned int sign : 1; } fg; float f; }; void print_float(float f) { union foo ff; ff.f = f; printf("%f: %d 0x%X 0x%X\n", f, ff.fg.sign, ff.fg.exponent, ff.fg.fraction); } int main(){ print_float(0.15625); return 0; }
看看维基百科上的单精度描述.我用那个例子和幻数0.15625.
union
也可用于实现具有多个备选方案的代数数据类型.我在O'Sullivan,Stewart和Goerzen的"真实世界Haskell"一书中找到了一个例子.在The discriminated union部分查看.
干杯!
" 联盟 "和" 结构 "是结构的C语言.谈论它们之间的"操作系统级别"差异是不合适的,因为如果您使用一个或另一个关键字,编译器会生成不同的代码.
是的,struct和union之间的主要区别与你所说的相同.Struct使用其成员的所有内存和union使用最大的成员内存空间.
但所有不同之处在于内存的使用需求.在我们利用信号的unix过程中可以看到联合的最佳用法.就像一个过程一次只能对一个信号起作用.所以一般声明将是:
union SIGSELECT { SIGNAL_1 signal1; SIGNAL_2 signal2; ..... };
在这种情况下,进程仅使用所有信号的最高内存.但如果在这种情况下使用struct,则内存使用量将是所有信号的总和.造成很大的不同.
总而言之,如果您知道一次访问任何一个成员,则应选择Union.
从技术上讲,意味着:
假设:主席=内存块,人=变量
结构:如果有3个人,他们可以相应地坐在椅子上.
联盟:如果有3个人只有一把椅子可以坐,所有人都需要在他们想坐的时候使用同一把椅子.
从技术上讲,意味着:
下面提到的程序深入探讨了结构和联合.
struct MAIN_STRUCT { UINT64 bufferaddr; union { UINT32 data; struct INNER_STRUCT{ UINT16 length; UINT8 cso; UINT8 cmd; } flags; } data1; };
用于联合的bufferaddr + sizeof(UNIT32)的总MAIN_STRUCT大小= sizeof(UINT64)用于填充的32位(取决于处理器架构)= 128位.对于结构,所有成员都连续获取内存块.
Union获取max size成员的一个内存块(这里是32位).在内部联合中,还有一个结构(INNER_STRUCT)其成员获得总大小为32位(16 + 8 + 8)的内存块.在联合中,可以访问INNER_STRUCT(32位)成员或数据(32位).
你拥有它,就是这样.但基本上,工会有什么意义呢?
您可以在不同类型的相同位置放置内容.你必须知道你在联合中存储的内容的类型(通常你把它放在struct
带有类型标记的...中).
为什么这很重要?不是为了空间收益.是的,你可以获得一些比特或做一些填充,但这不再是主要观点了.
这是为了类型安全,它使您能够进行某种"动态类型化":编译器知道您的内容可能具有不同的含义以及您在运行时如何解释它的确切含义.如果你有一个可以指向不同类型的指针,你必须使用一个联合,否则你的代码可能由于别名问题而不正确(编译器对自己说"哦,只有这个指针可以指向这种类型,所以我可以优化那些访问......",可能会发生坏事."
结构分配其中所有元素的总大小.
联合只分配与其最大成员所需的内存一样多的内存.