联合体和结构体类似但又不同,C语言中的联合体(Union)是一种特殊的数据类型,它允许在相同的内存位置存储不同的数据类型。联合体提供了一种方式,使得不同的数据类型可以共享同一段内存空间。但是,在任何给定的时刻,联合体只能存储它的成员之一的值;即联合体的所有成员从相同的内存地址开始。
联合体的声明
联合体使用union
关键字来声明,后跟联合体的名称和它的成员列表,成员列表用花括号{}
括起来,成员之间用逗号,
分隔。
union TestU{
int idata;
char cdata;
double ddata;
};
在这个例子中,TestT
是一个联合体,它可以存储一个int
、一个char或者一个double变量。但是,在任何时候,它只能存储这三种类型之一的值。
联合体的大小
联合体的大小至少是足够存储它最大成员所需的空间。由于联合体的所有成员都从相同的内存地址开始,因此它的总大小不会是所有成员大小的总和,而是它最大成员的大小。
#include<stdio.h>
struct TestT{
int idata;
char cdata;
double ddata;
};
union TestU{
int idata;
char cdata;
double ddata;
};
int main()
{
struct TestT t1;
union TestU u1;
printf("结构体t1的大小是:%d\n",sizeof(t1));
printf("联合体u1的大小是:%d\n",sizeof(u1));
return 0;
}
输出将是:
结构体t1的大小是:16
联合体u1的大小是:8
这就体现了联合体大小计算的特点,由于联合体TestU的成员中最大的是double型变量,占据8个字节的空间,因此整个联合体的大小是8个字节。而结构体大小的计算则是其内所有成员大小的和,即为16个字节。
访问联合体的成员
你可以通过联合体变量来访问它的成员,就像访问结构体的成员一样。但是,你需要确保在访问某个成员之前,该成员是最后被设置的成员,因为联合体不会跟踪当前存储的是哪个成员的值。
也就是说在对联合体内数据进行赋值时会进行覆盖。
#include<stdio.h>
union TestU{
int idata;
char cdata;
double ddata;
};
int main()
{
union TestU u1;
u1.idata = 10;
u1.cdata = 'a';
printf("u1.idata = %d",u1.idata);
return 0;
}
输出将是:
u1.idata = 97
从输出可以看到,我们对idata赋的值是10,而程序运行打印出来的是97也及时字符'a'对应的ASCII码的值,这体现了联合体的特点。
联合体成员的地址:
联合体内成员公用一个地址,与结构体有所不同。
#include<stdio.h>
struct TestT{
int idata;
char cdata;
double ddata;
};
union TestU{
int idata;
char cdata;
double ddata;
};
int main()
{
struct TestT t1;
union TestU u1;
printf("结构体t1.idata的地址是:%p\n",&t1.idata);
printf("结构体t1.cdata的地址是:%p\n",&t1.cdata);
printf("结构体t1.ddata的地址是:%p\n",&t1.ddata);
printf("联合体u1.idata的地址是:%p\n",&u1.idata);
printf("联合体u1.cdata的地址是:%p\n",&u1.cdata);
printf("联合体u1.ddata的地址是:%p\n",&u1.ddata);
return 0;
}
输出将是:
结构体t1.idata的地址是:000000000061FE10
结构体t1.cdata的地址是:000000000061FE14
结构体t1.ddata的地址是:000000000061FE18
联合体u1.idata的地址是:000000000061FE08
联合体u1.cdata的地址是:000000000061FE08
联合体u1.ddata的地址是:000000000061FE08
从输出可以看出,联合体内的成员公用的是一个地址,因此才会出现赋值会被覆盖的情况。
使用情景:
有若干个人员的数据,其中有学生和教师。学生的数据中包括:姓名、号码性别、职业、班级。教师的数据包括:姓名、号码、性别、职业、职务。要求用同一个表格来处理。
分析:人员的数据基本相同,主要是要区别出学生和老师的身份,根据身份的不同,统计的信息略有区别,因此可以考虑在结构体中设置一个联合体,将这些不同的信息放进联合体中,根据身份信息的不同,来赋给联合体中不同成员的不同值,这样可以节省内存空间。
#include<stdio.h>
#include<stdlib.h>
struct Person{
char name[32];
char sex[12];
int tele;
char zhiye;
union{
int class;
char subject[32];
}occup;
};
int main()
{
struct Person p[2];
int i;
for(i=0;i<2;i++){
printf("请输入职业:t代表老师,s代表学生\n");
scanf("%s",&(p[i].zhiye));
if(p[i].zhiye == 't'){
printf("请输入老师的姓名:\n");
scanf("%s",p[i].name);
printf("请输入老师的科目:\n");
scanf("%s",p[i].occup.subject);
}else if(p[i].zhiye == 's'){
printf("请输入学生的姓名:\n");
scanf("%s",p[i].name);
printf("请输入学生的班级:\n");
scanf("%d",&(p[i].occup.class));
}else{
printf("输入错误!!\n");
exit(1);
}
}
for(i=0;i<2;i++){
if(p[i].zhiye == 't'){
printf("%s老师教的科目是%s\n",p[i].name,p[i].occup.subject);
}else{
printf("%s学生的班级是%d\n",p[i].name,p[i].occup.class);
}
}
return 0;
}
这里涉及到了一些scanf相关的问题,在上面的代码中可以看到使用了多个scanf函数,其中有的使用了取地址符号&,有的没使用,其依据是:当你需要读取基本数据类型(如 int
, char
, float
等)的值时,你需要提供这些变量的地址,这时使用 &
。但是,当你读取数组或结构体的成员时,因为它们本身就是地址,所以不需要 &
。对于联合体,如果成员是基本数据类型,就需要使用 &
;如果成员是数组类型,就不需要 &
。因为zhiye变量和class变量是char和int基本型变量,因此使用了取地址符&。