0,
我们知道,如果需要保存一个整数,如:学号,定义一个int类型的变量
如果需要保存一个小数,如:成绩,定义一个 double类型的变量
如果需要保存一个字符串,如:姓名,定义一个字符数组 char name[50];
但是实际问题中,有些数据比较复杂,如:怎么保存一个“学生”数据呢?
没办法用一个单一的数据去保存,因为学生有很多属性。
所以c语言中,允许程序员自定义“组合类型” -》 构造类型
用来保存一些复杂的数据
-》
结构体
共用体
枚举
1,结构体
结构体是需要程序员自己去构造的一种类型
用 struct 关键字来构造
1.1 怎么构造/声明一个结构体类型
struct 结构体名
{
成员1类型 成员1名字;
成员2类型 成员2名字;
.....
成员n类型 成员n名字;
};
成员个数 >= 1即可
上面的语法就是声明了一个新的类型,新类型的名字叫做 : struct 结构体名
一般写在函数外面。如果有.h文件,就写在 .h文件里面
如:
struct test
{
int a;
double b;
};
声明了一个结构体类型,叫做 struct test。
声明:就是告诉编译器现在多了一个新的数据类型,-》 struct test
意思是说:从此刻起,数据类型除了 int double char long ......这些基本类型之外,我们多了
一个数据类型 -》 struct test
1.2 定义结构体类型的变量
怎么定义?符合定义变量的基本语法:
数据类型 变量名;
struct test t;
定义一个变量,名字叫做t,类型是 struct test ,变量t中包含了两个成员 a,b
1.3 如何访问结构体变量中的成员
使用 . 和 -> 这两个运算符
(1) 结构体变量名.成员变量名
t.a
t.b
t.a = 10;
t.b = 4.7;
printf("t.a=%d,t.b=%lf\n",t.a,t.b);
练习:
声明一个结构体类型 struct student ,再定义一个结构体变量,然后给各个成员赋值
(2) (*结构体指针).成员变量名
struct student * p = &t;//定义指针变量,指向结构体变量t
(*p).a
(*p).b
(3) 结构体指针->成员变量名
struct student * p = &t;//定义指针变量,指向结构体变量t
p->a
p->b
具体见代码
1.4 结构体变量初始化 ,用 {}
(1) 按照声明结构体类型时成员的顺序进行初始化,用逗号隔开
struct test t = {15,1.8};
struct student s = {1,85.5,"zhangsan"};
(2) 不按顺序,指定初始化某个成员
struct test t = {.b=2.5};
struct student s = {.id=1,.name="zhangsan"};
(3) 初始化结构体数组
struct student s[3] = {{1,80,"zhangsan"},{2,85,"lisi"},{3,70,"wangwu"}};
结构体变量之间的赋值,可以直接用 = 符号进行整体赋值
struct student s1 ={1,80,"zhangsan"};
struct student s2 ={2,85,"lisi"};
s1 = s2;
练习题:
定义一个 struct student数组并初始化,保存5个学生的信息,然后按照成绩进行排序(从小到大)
1.5 结构体各个成员在内存中的布局 -》字节对齐
最基本原则:各个成员按顺序存储
strcut test
{
int a;
double b;
};
struct test t;
printf("%lld\n",sizeof(t));//16 为什么???
因为构造类型存在字节对齐的问题。
字节对齐是指在内存中存储各种类型数据时,为了优化内存存储和访问效率,
需要把特定的数据类型的地址对齐到某个固定的边界上,通常是该数据类型大小的整数倍。
具体规则(以64位机器为例):
1,基本对齐原则
数据类型的起始地址必须是该数据类型大小的整数倍。
例如:
一个int类型的数据,地址必须是4的整数倍
一个double类型的数据,地址必须是8的整数倍
....
2,填充字节原则
为了满足基本对齐原则,在相邻的成员之间可能需要填充若干个字节,使得后续数据类型能够满足对齐规则
这些填充的数据没有意义
3,结构体整体对齐规则
对于结构体等构造类型,其对齐方式必须满足最基本类型成员对齐方式的要求。
并且该结构体的起始地址是最大基本类型成员大小的整数倍,
该结构体整体大小也是最大基本类型成员大小的整数倍
例如:如果一个结构体中有3个成员,分别是 int ,double ,char类型,那么该结构体
的起始地址肯定是 8的整数倍,并且结构体的整体大小也是8的整数倍
如果一个结构体中有3个成员,分别是 int ,double ,char [10]类型,那么该结构体
的起始地址肯定是 8的整数倍(char [10]不是基本类型,当作10个单独的char来看待),
并且结构体的整体大小也是8的整数倍
struct test1
{
char a;
short b;
int c;
};
sizeof(struct test1) -> 8
内存布局(*代表填充) -* -- ----
struct test2
{
short b;
int c;
char a;
};
sizeof(struct test2) -> 12
内存布局(*代表填充) --** ---- -***
struct test3
{
short b;
char d;
int c;
char a;
char e;
};
sizeof(struct test3) -> 12
内存布局(*代表填充) -- -* ---- - -**
struct student
{
int id;
double score;
char name[20];
};
sizeof(struct student) -> 40
---- **** -------- 20--- ****
struct student
{
char name[20];
int id;
double score;
};
sizeof(struct student) -> 32
20--- ---- --------
struct test4
{
double data;
char s[10];
struct student stu;
};
sizeof(struct test4) ->
3,共用体(联合体)
也是构造类型, 用关键字 union 声明
声明语法:
union 共用体名字
{
成员1类型 成员1名字;
成员2类型 成员2名字;
....
成员n类型 成员n名字;
};
访问共用体成员的方法和结构体完全一样
和结构体的区别在于:
结构体各个成员都有独立的内存空间,互不影响
共用体各个成员公用一块内存,不管有几个成员,都是共用一块内存
公用一块内存怎么理解?各个成员的首地址相同
union test
{
char a; //1字节
int b; //4字节
double c;//8字节
};
三个成员a,b,c首地址相同
union test t;//定义共用体变量
printf("%p\n%p\n%p\n%p\n",&t,&t.a,&t.b,&t.c