C语言中Union与struct知识汇总
结构体struct
今天在看程序时,对Union的应用比较陌生,特此对union 和struct进行梳理和分析,以后就能快速应用啊,希望今天的整理,能帮到同样对此有困惑的你。
定义结构体的三种方式
#include<stdio.h>
//定义结构体的三种方式
struct A{ //方式一
int a;
long *b;
char c[20];
};
struct{ //方式二
int a;
long *b;
char c[20];
}s1;
typedef struct B{ //方式三
int a;
long *b;
char c[20];
}Byt;
int main(){
struct A age;
age.a=4;//初始化方式一
s1.a=6; //初始化方式二
Byt s2;
s2.a=7;//初始化方式三
/*--------------------------------------------------------------------------------------------------------------------------
结构体大小计算
结构体默认的字节对齐一般满足三个准则:
- 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
- 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
- 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)
结构体的自引用
struct B{
int a;
struct B b;
int c;
};
这种引用是不合法的,因为b是一个完整的结构,第二个成员又是另一个完整的结构,还包括她自己的成员,这样会循环下去无法及所结构体的大小。
struct B{
int a;
struct B *b;
int c;
};
这种声明是合法的,b现在是一个指针它所占的字节数是已知的,可以计算出结构体的大小,这种自引用是合法的。
结构体数组与结构体指针
(1)结构体数组
一个结构体可以用来存储一组信息,但是对于多组信息,我们就需要用数组在来处理。此时,每一个结构体数组的元素,就是一个结构体变量。
#include<stdio.h>
//申请记录学生信息的结构体
struct student{
char name[6];
int age;
char Idcard[18];
};
int main(){
struct student stu[10];
int i=0;
for(i=0;i<10;i++){
stu[i].age=10+i;
printf("%d\n",stu[i].age);
}
return 0;
}
(2)结构体指针
一个结构体变量的指针就是该结构体变量所占据内存段的起始地址。因此可以设一个指针变量指向结构体变量,此时该指针变量的值是结构体变量的起始地址。同时指针变量也可以用来指向结构体数组中的元素。借用C语言中文网上的例程如下:
#include <stdio.h>
#include <string.h>
struct AGE
{
int year;
int month;
int day;
};
struct STUDENT
{
char name[20]; //姓名
int num; //学号
struct AGE birthday; //生日
float score; //分数
};
int main(void)
{
struct STUDENT student1; /用struct STUDENT结构体类型定义结构体变量student1/
struct STUDENT *p = NULL; /定义一个指向struct STUDENT结构体类型的指针变量p/
p = &student1; /p指向结构体变量student1的首地址, 即第一个成员的地址/
strcpy((*p).name, “小明”); //(*p).name等价于student1.name
(*p).birthday.year = 1989;
(*p).birthday.month = 3;
(*p).birthday.day = 29;
(*p).num = 1207041;
(*p).score = 100;
printf(“name : %s\n”, (*p).name); //(*p).name不能写成p
printf(“birthday : %d-%d-%d\n”, (*p).birthday.year, (*p).birthday.month, (*p).birthday.day);
printf(“num : %d\n”, (*p).num);
printf(“score : %.1f\n”, (*p).score);
return 0;
}
联合体Union
联合体与结构体之间的区别是:结构体的各个成员会占用不同的内存,相互之间无影响。而联合体的所有成员公用一段内存,修改一个成员会影响其余所有成员。结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙),联合体占用的内存等于最长的成员占用的内存。联合体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。
联合体的定义
第一种方式是先定义联合体,在创建变量:
union data{
int n;
char ch;
double f;
};
union data a, b, c;
第二种方式是在定义联合体时创建变量:
union data{
int n;
char ch;
double f;
} a, b, c;
第三种是如果不再定义新的变量,也可以将共用体的名字省略:
union{
int n;
char ch;
double f;
} a, b, c;
联合体应用
在此借用C语言中文网上的例程来讲解Union的特点:
#include<stdio.h>
union data{
int n;
char ch;
short m;
};
int main(){
union data a;
printf("%d, %d\n", sizeof(a), sizeof(union data) );
a.n = 0x40;
printf("%X, %c, %hX\n", a.n, a.ch, a.m);
a.ch = '9';
printf("%X, %c, %hX\n", a.n, a.ch, a.m);
a.m = 0x2059;
printf("%X, %c, %hX\n", a.n, a.ch, a.m);
a.n = 0x3E25AD54;
printf("%X, %c, %hX\n", a.n, a.ch, a.m);
return 0;
}
运行结果如下:
4, 4
40, @, 40
39, 9, 39
2059, Y, 2059
3E25AD54, T, AD54
通过运行结果我们可以看出,Union中首地址固定,各成员变量公用一段内存,在同一时刻,联合体的变量按自身内存大小公用这段内存。因此,Union可以解决结构体中部分成员共用一段内存的问题,进行Union和struct之间的相互嵌套。下面再应用一个案例来说明,详细网址:http://c.biancheng.net/view/2035.html
共用体在一般的编程中应用较少,在单片机中应用较多。对于 PC 机,经常使用到的一个实例是: 现有一张关于学生信息和教师信息的表格。学生信息包括姓名、编号、性别、职业、分数,教师的信息包括姓名、编号、性别、职业、教学科目。请看下面的表格:
f 和 m 分别表示女性和男性,s 表示学生,t 表示教师。可以看出,学生和教师所包含的数据是不同的。现在要求把这些信息放在同一个表格中,并设计程序输入人员信息然后输出。
如果把每个人的信息都看作一个结构体变量的话,那么教师和学生的前 4 个成员变量是一样的,第 5 个成员变量可能是 score 或者 course。当第 4 个成员变量的值是 s 的时候,第 5 个成员变量就是 score;当第 4 个成员变量的值是 t 的时候,第 5 个成员变量就是 course。
经过上面的分析,我们可以设计一个包含共用体的结构体,请看下面的代码:
#include <stdio.h>
#include <stdlib.h>
#define TOTAL 4 //人员总数
struct{
char name[20];
int num;
char sex;
char profession;
union{
float score;
char course[20];
} sc;
} bodys[TOTAL];
int main(){
int i;
//输入人员信息
for(i=0; i<TOTAL; i++){
printf(“Input info: “);
scanf(”%s %d %c %c”, bodys[i].name, &(bodys[i].num), &(bodys[i].sex), &(bodys[i].profession));
if(bodys[i].profession == ‘s’){ //如果是学生
scanf("%f", &bodys[i].sc.score);
}else{ //如果是老师
scanf("%s", bodys[i].sc.course);
}
fflush(stdin);
}
//输出人员信息
printf("\nName\t\tNum\tSex\tProfession\tScore / Course\n");
for(i=0; i<TOTAL; i++){
if(bodys[i].profession == ‘s’){ //如果是学生
printf("%s\t%d\t%c\t%c\t\t%f\n", bodys[i].name, bodys[i].num, bodys[i].sex, bodys[i].profession, bodys[i].sc.score);
}else{ //如果是老师
printf("%s\t%d\t%c\t%c\t\t%s\n", bodys[i].name, bodys[i].num, bodys[i].sex, bodys[i].profession, bodys[i].sc.course);
}
}
return 0;
}
运行结果:
Input info: HanXiaoXiao 501 f s 89.5↙
Input info: YanWeiMin 1011 m t math↙
Input info: LiuZhenTao 109 f t English↙
Input info: ZhaoFeiYan 982 m s 95.0↙
Name Num Sex Profession Score / Course
HanXiaoXiao 501 f s 89.500000
YanWeiMin 1011 m t math
LiuZhenTao 109 f t English
ZhaoFeiYan 982 m s 95.000000