1.11 共用体
共用体是C语言的另外一种构造类型,与前面介绍的结构体类似。共用体也由基本数据结构组合而成,但是共用体和结构体却有本质区别,因为结构体中的每个成员都占用存储单元,所以结构体所占用的内存大小为所有成员各自占用的内存大小之和,而共用体占用的内存大小由其成员中占用内存最大的那个决定,所有的成员都占用同一个起始地址和同一段内存空间。对于共用体变量,在某一时刻,只能存储其某一成员的信息。
共用体类型的定义形式为:
union共用体名{
成员类型成员名;
……
成员类型成员名;
};
共用体名是定义的共用体类型的标识符,同样要遵循自定义标识符的命名规则。而其中的成员类型可以是任何基本数据类型,也可以是指针、数组等复合数据类型,还可以是结构体或者共用体。
下面来看几种共用体变量的定义方法。
union共用体名{
成员类型成员名;
……
成员类型成员名;
}共用体变量1,共用体变量2……;
也可以省略掉共用体名。
union{
成员类型成员名;
……
成员类型成员名;
}共用体变量1,共用体变量2……;
还可以先定义共用体类型,再定义共用体变量。
union共用体名{
成员类型成员名;
……
成员类型成员名;
};
union共用体名共用体变量1,共用体变量2……;
我们发现共用体和结构体不管是在定义方式上还是在变量定义上都非常相似,但是它们之间有本质的区别,为了使读者更好地区别它们,我们通过下面的一段代码来看结构体和共用体之间究竟有什么样的区别。
include<stdio.h>
struct str{
int a;
int b;
int c;
};
union uni{
char a;
int b;
int c;
};
void main()
{
struct str x;
union uni y;
printf("结构体所占的内存大小为%d字节\n",sizeof(x));
printf("结构体中成员变量a的地址为%d\n",&x.a);
printf("结构体中成员变量b的地址为%d\n",&x.b);
printf("结构体中成员变量c的地址为%d\n\n",&x.c);
printf("共用体所占的内存大小为%d字节\n",sizeof(y));
printf("共用体中成员变量a的地址为%d\n",&y.a);
printf("共用体中成员变量b的地址为%d\n",&y.b);
printf("共用体中成员变量c的地址为%d\n",&y.c);
return;
}
运行结果:
结构体所占的内存大小为12字节
结构体中成员变量a的地址为1245048
结构体中成员变量b的地址为1245052
结构体中成员变量c的地址为1245056
共用体所占的内存大小为4字节
共用体中成员变量a的地址为1245044
共用体中成员变量b的地址为1245044
共用体中成员变量c的地址为1245044
分析上面的代码,sizeof操作符的作用就是计算结构体变量x和共用体变量y所占用的内存空间。通过运行结果我们发现,x所占用的内存空间大小为12字节,刚好等于sizeof(a)+sizeof(b)+sizeof(c)。正如上面所介绍的,结构体的每个成员都有自己的存储空间,每个成员的起始地址都不相同,它所占用的内存大小等于各个成员所占用的内存大小之和。y所占用的内存大小为4字节,而sizeof(a)+sizeof(b)+sizeof(c)=9字节,即共用体所占用的内存大小并不等于它的每个成员所占用的内存大小之和,正如前面所讲的,共同体所占用的内存大小就等于其占用内存最大的成员所占用的内存大小。y中占用内存最大的为int型变量b和int型变量c,占用4字节,所以共用体占用的内存大小为4字节,并且共用体中每个成员的起始地址都相同,它们共用一个存储空间。我们可以用图1-10和图1-11来说明结构体和共用体在内存中的结构。
图 1-10 结构体x的内存结构
图 1-11 共用体y的内存结构
可以通过下面的代码来验证图1-10和图1-11中x和y的内存结构。
include<stdio.h>
struct str{
int a;
int b;
int c;
};
union uni{
char a;
int b;
int c;
};
void main()
{
struct str x;
union uni y;
x.a=0x2a3d;
x.b=0xc4df;
x.c=0x5bac;
printf("结构体中成员变量a的值为%x\n",x.a);
printf("结构体中成员变量b的值为%x\n",x.b);
printf("结构体中成员变量c的值为%x\n\n",x.c);
y.a=0x1345;
y.b=0x1345;
y.c=0xb548;
printf("共用体中成员变量a的值为%x\n",y.a);
printf("共用体中成员变量b的值为%x\n",y.b);
printf("共用体中成员变量c的值为%x\n",y.c);
return;
}
运行结果:
结构体中成员变量a的值为2a3d
结构体中成员变量b的值为c4df
结构体中成员变量c的值为5bac
共用体中成员变量a的值为48
共用体中成员变量b的值为b548
共用体中成员变量c的值为b548
上述代码先对结构体变量x的每个成员赋初值,然后输出,结果和初始值完全一致,但是当对共用体赋初值并输出的时候,其结果都是最后一次对共用体变量y中成员c的赋值。由此也可以看出,共用体是共享存储空间的,对其成员变量赋值会覆盖之前对共用体中变量所赋的值。当打印a的值时,因为它在内存中占用的是最低字节的内存,所以打印出来的是最后对共用体变量y的成员c赋值的低字节部分48。
接下来我们用结构体和共用体嵌套定义一个自定义类型来登记学校老师和学生的信息。
include<stdio.h>
include<stdlib.h>
struct infor{
char name[20];
char sex[10];
int age;
char identity;
union otherinf{
struct{
char profession[10];
char department[20];
double salary;
}teacher;
struct{
char num[20];
char department[20];
char major[20];
}student;
}perinf;
};
void print(struct infor per[],int n)
{
int i;
for(i=0;i<n;i++)
{
printf("姓名:%s\t性别:%s\t年龄:%d\t",per[i].name,per[i].sex,per[i].age);
if('s'==per[i].identity)
{
printf("学生的学号:%s\t所属的院系:%s\t专业:%s\n",per[i].perinf.student.num,
per[i].perinf.student.department,per[i].perinf.student.major);
}
else
{
printf("教师的职称:%s\t所属的院系:%s\t月薪:%6.2f",per[i].perinf.teacher.profession,
per[i].perinf.teacher.department,per[i].perinf.teacher.salary);
}
}
return;
}
void input(struct infor per[],int n)
{
int i;
for(i=0;i<n;i++)
{
printf("请依次输入姓名,性别,年龄,身份:");
scanf("%s%s%d%s",&per[i].name,&per[i].sex,&per[i].age,&per[i].identity);
if('s'==per[i].identity)
{
printf("请依次输入学生的学号,所属的院系,专业:");
scanf("%s%s%s",&per[i].perinf.student.num,&per[i].perinf.student.
department,&per[i].perinf.student.major);
}
else if('t'==per[i].identity)
{
printf("请依次输入教师的职称,所属的院系,月薪:");
scanf("%s%s%lf",&per[i].perinf.teacher.profession,&per[i].
perinf.teacher.department,&per[i].perinf.teacher.salary);
}
else
{
printf("输入出错!\n");
exit(0);
}
}
return;
}
void main()
{
struct infor per[2];
input(per,2);
print(per,2);
return;
}
运行结果:
请依次输入姓名,性别,年龄,身份:张丽玲女35 t
请依次输入教师的职称,所属的院系,月薪:教授电信学院8900
请依次输入姓名,性别,年龄,身份:张晓明男22 s
请依次输入学生的学号,所属的院系,专业:202060639电信学院信息与系统
姓名:张丽玲 性别:女年龄:35教师的职称:教授所属的院系:电信学院 月薪:890
0.00
姓名:张晓明 性别:男年龄:22 学生的学号:202060639 所属的院系:电信学院
专业:信息与系统
分析上面的代码,其中定义了一个登记学生和教师信息的结构体,结构体中包含姓名(name)、性别(sex)、年龄(age)和身份(identity),其中身份的取值为's'和't',分别代表学生和教师。在结构体中嵌套了共用体,共用体中又嵌套了两个结构体,如果身份为学生,那么选择共用体中的学生信息结构体类型成员,包括学号(num)、院系(department)、专业(major)相关信息;如果身份为教师,那么选择共用体中的教师信息结构体类型成员,包括职称(profession)、院系(department)、月薪(salary)相关信息。根据身份的不同,在共用体中选择不同结构体类型的成员。在使用结构体和共用体进行嵌套的时候要尤其注意其中成员的引用方法,从最外层类型变量开始引用它的成员,如果它的成员是共用体或者结构体类型的变量,那么接着以共用体或者结构体类型变量的方式引用它的成员变量。