C语言中除了结构体,还有一种很重要的结构,叫做共用体(Union).
共用体定义
共用体(Union)是C语言中一个特殊的数据类型,可以在相同的内存位置存储不同的数据类型,可以定义一个或者多个成员变量的共用体,但是在指定时刻,只能有一个成员变量的值是完整的有效值.
union [union tag]
{
member definition;
member definition;
...
member definition;
} [one or more union variables];
共用体内存开辟
对于共用体来讲,虽然可以定义多个类型的成员变量,但是系统会根据最大的成员变量进行内存开辟,同时只能有一个成员变量在使用,多个成员变量不能同时使用.这是共用体与结构体非常重要的区别.定义共用体:
union Data
{
int i;
float f;
char str[20];
} data;
查看共用体的内存占用:
printf( "Memory size occupied by data : %d\n", sizeof(data));
输出结果:
Memory size occupied by data : 20
由此可见,联合体的内存分配是使用是根据成员变量中需要存储空间最大的成员变量来确定的,而不是像结构体那样,将为每一次成员变量开辟内存空间.
共用体储存原则
共用体虽然可以定义多个变量,但是由于多个变量之间需要内存共享,所以有限的存储空间只能保证在在某一时刻只有一个变量的值是完整的有效值,也就是最近一次存储的成员变量的值是完整的有效值,其余的成员变量的值将会被覆盖.
union Data data;
printf( "Memory size occupied by data : %lu\n", sizeof(data));
data.i = 10;
printf( "data.i : %d\n", data.i);
data.f = 220.5;
printf( "data.f : %f\n", data.f);
strcpy( data.str, "C Programming");
printf( "data.i : %d\n", data.i);
printf( "data.f : %f\n", data.f);
printf( "data.str : %s\n", data.str);
输出结果:
Memory size occupied by data : 20
data.i : 10
data.f : 220.500000
data.i : 1917853763
data.f : 4122360580327794860452759994368.000000
data.str : C Programming
共用体与结构体联合使用
由于在结构体中,可以通过通过":"来单独定义各个成员变量所占据的bit位数,结合联合体会根据成员变量中所需内存空间最大的成员变量进行内存开辟,于是就产生了个神奇的用法.例如,红黄蓝三原色
union Color {
unsigned int value;
struct {
unsigned char Red;
unsigned char Green;
unsigned char Blue;
};
};
由于共用体会根据成员变量中所需内存最大的成员变量的大小来开辟内存,所以共用体只需要开辟unsigned int类型所需要的内存大小即可.而unsigned char类型的三个变量会在内存中依次存储.
union Color pix;
pix.Blue = 0X33;
pix.Green = 0X22;
pix.Red = 0X11;
printf("%X\r\n",pix.value);
输出结果:
6B332211
可以看出,三个成员变量在内存中依次存储,相当于使用了一个unsigned int类型的变量存储了三个unsigned char类型的变量,而且可以方便地存取,如果需要单个原色只需要使用(.)运算符直接访问属性,如果需要直接使用三原色,只需要取出value的值抹掉高位的一个字节即可(unsigned int占用四个字节,但是结构体中的成员变量只使用了三个字节,第四个字节是未操作的数据).或许你会忍不住吐槽,明明直接使用结构体就可以搞定的事情你搞这么麻烦又啥子意义??
只是因为结构体中的成员变量在基本类型都有定义,所以显的这种组合有点多余.但是如果想要在一个基本数据类型中存储多种信息,并且这种信息只需要几个bit就可以完成的话,这种结构就具有重要意义:
union isa_t {
uintptr_t bits;
struct {
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
};
};
这样的话只需要,只是用了一个uintptr_t(unsigned long)类型的成员变量,就可以完成9个变量的完美存取,不仅大大节约了内存空间,简化了存取的流程,加快了存取效率,而且便于理解和阅读.这种定义方式成为位域结构.