联合体(union)在C语言中是一个特殊的数据类型,能够存储不同类型的数据在同一个内存位置。可以定义一个联合体使用许多成员,但只有一个部件可以包含在任何时候给定的值。联合体会提供使用相同的存储器位置供多用途的有效方式。
定义联合体
要定义联合体,必须使用union语句很相似于定义结构。联合体声明中定义了一个新的数据类型,程序不止一个成员。联合体声明的格式如下:
1
2
3
4
5
6
7
|
union [ union tag]
{
member definition;
member definition;
...
member definition;
} [one or more union variables];
|
union标签是可选的,每个成员的定义是一个正常的变量定义,如 int i; 和 float f; 或任何其他有效的变量的定义。在联合体定义的结尾,最后分号之前,可以指定一个或多个变量的联合,但它是可选的。这里定义一个名为数据联合类型有三个成员 i, f, 和 str:
1
2
3
4
5
6
|
union Data
{
int i;
float f;
char str[20];
} data;
|
现在,数据类型的变量可以存储的整数,一个浮点数,或字符的字符串。这意味着一个单可变结构即相同的存储单元可用于存储多个类型的数据。可以使用任何内置或用户定义的数据类型根据需要在联合里面。
通过union所占用的内存将大到足以容纳联合体的最大成员。例如,在上面的例子中的数据类型将占用20个字节的存储空间,因为这是通过文字串所占用的最大空间。以下将显示由上述联合共占用内存大小的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#include <stdio.h>
#include <string.h>
union Data
{
int i;
float f;
char str[20];
};
int main( )
{
union Data data;
printf ( "Memory size occupied by data : %d
", sizeof (data));
return 0;
}
|
让我们编译和运行上面的程序,这将产生以下结果:
1
|
Memory size occupied by data : 20
|
访问联合体成员
要访问联合体的任何成员,我们使用成员访问运算符(.)。成员访问运算符编码为联合体变量名和成员,访问时使用union关键字定义联合体类型的变量。以下为例子来解释联合体的用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
#include <stdio.h>
#include <string.h>
union Data
{
int i;
float f;
char str[20];
};
int main( )
{
union Data data;
data.i = 10;
data.f = 220.5;
strcpy ( data.str, "C Programming" );
printf ( "data.i : %d
", data.i);
printf ( "data.f : %f
", data.f);
printf ( "data.str : %s
", data.str);
return 0;
}
|
让我们编译和运行上面的程序,这将产生以下结果:
1
2
3
|
data.i : 1917853763
data.f : 4122360580327794860452759994368.000000
data.str : C Programming
|
在这里,我们可以看到,联合体成员i 和f 值被损坏,因为分配给变量终值已占用的内存位置,如果str成员的值被很好的打印的原因。现在,让我们来再一次看看同样的例子,我们将使用一个变量在同一时间,它是联合体的主要目的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
#include <stdio.h>
#include <string.h>
union Data
{
int i;
float f;
char str[20];
};
int main( )
{
union Data data;
data.i = 10;
printf ( "data.i : %d
", data.i);
data.f = 220.5;
printf ( "data.f : %f
", data.f);
strcpy ( data.str, "C Programming" );
printf ( "data.str : %s
", data.str);
return 0;
}
|
让我们编译和运行上面的程序,这将产生以下结果:
1
2
3
|
data.i : 10
data.f : 220.500000
data.str : C Programming
|
这里,所有的成员都得到打印得非常好,因为一个部件被一次使用。
应用场合
当多个数据需要共享内存或者多个数据每次只取其一时,可以利用联合体(union)。在C Programming Language 一书中对于联合体是这么描述的:
1)联合体是一个结构;
2)它的所有成员相对于基地址的偏移量都为0;
3)此结构空间要大到足够容纳最"宽"的成员;
4)其对齐方式要适合其中所有的成员;
下面解释这四条描述:
由于联合体中的所有成员是共享一段内存的,因此每个成员的存放首地址相对于于联合体变量的基地址的偏移量为0,即所有成员的首地址都是一样的。为了使得所有成员能够共享一段内存,因此该空间必须足够容纳这些成员中最宽的成员。对于这句“对齐方式要适合其中所有的成员”是指其必须符合所有成员的自身对齐方式。
下面举例说明:
如联合体
1
2
3
4
5
6
|
union U
{
char s[9];
int n;
double d;
};
|
s占9字节,n占4字节,d占8字节,因此其至少需9字节的空间。然而其实际大小并不是9,用运算符sizeof测试其大小为16.这是因为这里存在字节对齐的问题,9既不能被4整除,也不能被8整除。因此补充字节到16,这样就符合所有成员的自身对齐了。从这里可以看出联合体所占的空间不仅取决于最宽成员,还跟所有成员有关系,即其大小必须满足两个条件:1)大小足够容纳最宽的成员;2)大小能被其包含的所有基本数据类型的大小所整除。
测试程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
/*测试联合体 2011.10.3*/
#include <iostream>
using namespace std;
union U1
{
char s[9];
int n;
double d;
};
union U2
{
char s[5];
int n;
double d;
};
int main( int argc, char *argv[])
{
U1 u1;
U2 u2;
printf ( "%d\n" , sizeof (u1));
printf ( "%d\n" , sizeof (u2));
printf ( "0x%x\n" ,&u1);
printf ( "0x%x\n" ,&u1.s);
printf ( "0x%x\n" ,&u1.n);
printf ( "0x%x\n" ,&u1.d);
u1.n=1;
printf ( "%d\n" ,u1.s[0]);
printf ( "%lf\n" ,u1.d);
unsigned char *p=(unsigned char *)&u1;
printf ( "%d\n" ,*p);
printf ( "%d\n" ,*(p+1));
printf ( "%d\n" ,*(p+2));
printf ( "%d\n" ,*(p+3));
printf ( "%d\n" ,*(p+4));
printf ( "%d\n" ,*(p+5));
printf ( "%d\n" ,*(p+6));
printf ( "%d\n" ,*(p+7));
return 0;
}
|
输出结果为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
16
8
0x22ff60
0x22ff60
0x22ff60
0x22ff60
1
0.000000
1
0
0
0
48
204
64
0
请按任意键继续. . .
|
对于sizeof(u1)=16。因为u1中s占9字节,n占4字节,d占8字节,因此至少需要9字节。其包含的基本数据类型为char,int,double分别占1,4,8字节,为了使u1所占空间的大小能被1,4,8整除,则需填充字节以到16,因此sizeof(u1)=16.
对于sizeof(u2)=8。因为u2中s占5字节,n占4字节,d占8字节,因此至少需要8字节。其包含的基本数据类型为char,int,double分别占1,4,8字节,为了使u2所占空间的大小能被1,4,8整除,不需填充字节,因为8本身就能满足要求。因此sizeof(u2)=8。
从打印出的每个成员的基地址可以看出,联合体中每个成员的基地址都相同,等于联合体变量的首地址。
对u1.n=1,将u1的n赋值为1后,则该段内存的前4个字节存储的数据为00000001 00000000 00000000 00000000
因此取s[0]的数据表示取第一个单元的数据,其整型值为1,所以打印出的结果为1.
至于打印出的d为0.000000愿意如下。由于已知该段内存前4字节的单元存储的数据为00000001 00000000 00000000 00000000,从上面打印结果48,204,64,0可以知道后面4个字节单元中的数据为00110000 11001100 01000000 00000000,因此其表示的二进 制浮点数为
00000000 01000000 11001100 00110000 00000000 00000000 00000000 00000001
对于double型数据,第63位0为符号位,62-52 00000000100为阶码,0000 11001100 00110000 00000000 00000000 00000000 00000001为尾数,根据其值知道尾数值约为0,而阶码为4-1023=-1019,因此其表示的浮点数为1.0*2^(-1019)=0.00000000000......,因此输出结果为0.000000。