结构体
1、结构体概述
将多种数据结构封装在一起 形成新的结构胶结构体
每种数据结构 都有自己独立的空间。
结构体的关键字struct

2、结构体变量的初始化
//结构体变量的初始化必 须遵循成员的刚
struct stu lucy = {100,"lucy" };
struct stu lucy;
//使用memset给结构体变量清空
memset(&lucy, 0, sizeof(lucy));
struct stu lucy;
memset(&lucy, 0,sizeof(lucy));
//&Lucy. num取的是lucy 中num成员的地址
scanf("%d %s",&lucy.num, lucy.name) ;
printf("%d %s\n", lucy.num,lucy.name);
//可以单独操作结构体中的成员
struct stu lucy{100,"lucy"};
lucy.num += 100 ;
//字符串数组必须使用字符串操作函数进行操作
strcpy(lucy.name , "bob");3、结构体嵌套结构体

4、结构体数组
1、结构体数组:本质是数组 每个元素是结构体
struct stu2 edu[5] = {{100, "lucy"}, {101,'bob"}, {102,tom"},{103,” 德玛西亚"},{104, "小炮"}};2、键盘给结构体赋值
scanf("%d %s", &edu[i].num ,edu[i].name);5、结构体指针变量
1、指针成员:指针变量 作为结构体中的成员

2、结构体数组元素的指针变量
1 void input_stu_array(struct stu *arr, int n)
2 {
3 printf("请输入%d个学员的信息num name\n", n);
4 int i = 0;
5 for (i = 0; i < n; i++)
6 {
7 printf("请输入第%d个学生的信息:", i + 1);
8 //scanf("%d %s", &arr[i].num, arr[i].name);
9 scanf("%d %s", &(arr + i)‐>num, (arr + i)‐>name);
10 }
11 return;
12 }
// 结构体里按照num排序
13 void sort_stu_array(struct stu *arr, int n)
14 {
15 int i = 0;
16 for (i = 0; i < n ‐ 1; i++)
17 {
18 int min = i;
19 int j = min + 1;
20 for (; j < n; j++)
21 {
22 //if(strcmp(arr[min].name, arr[j].name) > 0)
23 if (arr[min].num > arr[j].num)
24 min = j;
25 }
26
27 if (i != min)
28 {
29 struct stu tmp = arr[min];
30 arr[min] = arr[i];
31 arr[i] = tmp;
32 }
33 }
34 return;
35 }
36 void print_stu_array(struct stu *arr, int n)
37 {
38 int i = 0;
39 for (i = 0; i < n; i++)
40 {
41 // printf("%d %s\n", arr[i].num, arr[i].name);
42 printf("%d %s\n", (arr + i)‐>num, (arr + i)‐>name);
43 }
44 return;
45 }
46 void test02()
47 {
48 struct stu edu[5];
49 memset(edu, 0, sizeof(edu));
50 int n = sizeof(edu) / sizeof(edu[0]);
51
52 //封装函数获取键盘输入
53 input_stu_array(edu, n);
54
55 //对结构体数组排序
56 sort_stu_array(edu, n);
57
58 //遍历结构体数组的内容
59 print_stu_array(edu, n);
60 }6、结构体的指针成员
1、指针成员:指针变量 作为结构体中的成员

文字常量不可以去写! 要不然就gg 要不然就放到堆区里面去

2、结构体的指针成员 指向堆区

1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 struct stu
5 {
6 int num;
7 char *name; //指针成员
8 };
9 void test01()
10 {
11 struct stu lucy;
12 lucy.num = 100;
13 //让name指向堆区 1块 128B
14 lucy.name = (char *)calloc(1, 128);
15 //此处造成内存泄漏 name先指向堆区 然后name又指向文字常量区 丢失堆区空间地址
16 //lucy.name = "hello world";
17 strcpy(lucy.name, "hello world");
18 printf("%d %s\n", lucy.num, lucy.name);
19
20 printf("%c\n", lucy.name[1]);
21 lucy.name[1] = 'E';
22 printf("%d %s\n", lucy.num, lucy.name);
23
24 //释放lucy.name指向的堆区空间
25 if (lucy.name != NULL)
26 {
27 free(lucy.name);
28 lucy.name = NULL;
29 }
30 }
31 int main(int argc, char const *argv[])
32 {
33 test01();
34 return 0;
35 }3、相同类型的结构体变量可以整体赋值
如果结构体中没有指针成员 赋值 不会出现浅拷贝。
如果结构体中有指针成员 赋值 容易造成浅拷贝

struct stu lucy;
lucy.num = 100;
//让name指向堆区
lucy.name = (char *)calloc(1, 128);
strcpy(lucy.name, "hello world");
printf("%d %s\n", lucy.num, lucy.name);
struct stu bob;
bob = lucy;//浅拷贝 释放空间的时候会出现问题!如果结构体中有指针成员 尽量使用深拷贝。
1 struct stu bob;
2 bob.name = (char *)calloc(1, 128);
3 bob.num = lucy.num;
4 strcpy(bob.name, lucy.name);4、结构体在堆区 结构体的指针成员指向堆区
关系到空间释放的顺序

1 void test02()
2 {
3 //结构体本身在堆区
4 struct stu *p = NULL;
5 p = (struct stu *)calloc(1, sizeof(struct stu));
6
7 //为结构体中的指针成员 申请堆区空间
8 p‐>name = (char *)calloc(1, 128);
9
10 //给结构体中的成员赋值
11 p‐>num = 100;
12 strcpy(p‐>name, "hello world");
13
14 printf("%d %s\n", p‐>num, p‐>name);
15
16 //先释放成员空间
17 if (p‐>name != NULL)
18 {
19 free(p‐>name);
20 p‐>name = NULL;
21 }
22
23 //在释放结构体的空间
24 if (p != NULL)
25 {
26 free(p);
27 p = NULL;
28 }
29 }5、结构体指针数组在堆区、结构体在堆区、结构指针成员在堆区

1 void test03()
2 {
3 //给结构体指针数组申请堆区空间
4 struct stu **arr = NULL;
5 arr = (struct stu **)calloc(5, sizeof(struct stu *));
6
7 //给指针数组中的每个元素 申请结构体堆区空间
8 int i = 0;
9 for (i = 0; i < 5; i++)
10 {
11 arr[i] = (struct stu *)calloc(1, sizeof(struct stu));
12 //每个结构体中的name成员申请堆区空间
13 arr[i]‐>name = (char *)calloc(1, 32);
14
15 //赋值
16 arr[i]‐>num = 100 + i;
17 sprintf(arr[i]‐>name, "姓名%d", i + 1);
18 }
19
20 //遍历
21 for (i = 0; i < 5; i++)
22 {
23 printf("%d %s\n", arr[i]‐>num, arr[i]‐>name);
24 }
25
26 //释放arr中的每个元素 以及每个元素中的name
27 for (i = 0; i < 5; i++)
28 {
29 //先释放结构体中的name的指向
30 if (arr[i]‐>name != NULL)
31 {
32 free(arr[i]‐>name);
33 arr[i]‐>name = NULL;
34 }
35
36 //释放结构体
37 if (arr[i] != NULL)
38 {
39 free(arr[i]);
40 arr[i] = NULL;
41 }
42 }
43 //释放整个arr指针数组
44 if (arr != NULL)
45 {
46 free(arr);
47 arr = NULL;
48 }
49
50 return;
51 }7、结构体的对齐规则
1、自动对齐规则

char 1B int 4B 但是这个结构体是8B 自动对齐了
1、确定分配单位(一行分配多少字节)
结构体中最大的基本类型 长度决定。
2、确定成员的偏移量
成员偏移量 = 成员自身类型的整数倍。
3、收尾工作
结构体的总大小 = 分配单位整数倍
案例1:画出以下结构体的对齐
1 struct Data
2 {
3 char a;
4 short b;
5 int c;
6 char d;
7 short e;
8 };
2、结构体嵌套结构体 自动对齐规则
1、确定分配单位(一行分配多少字节)
所有结构体中最大的基本类型 长度决定。
2、确定成员的偏移量
普通成员偏移量 = 成员自身类型的整数倍。
被嵌套的结构体整体偏移量 = 该结构体中最大的基本类型整数倍
3、收尾工作
结构体成员大小 = 该结构体中最大的基本类型整数倍
结构体的总大小 = 分配单位整数倍
案例


3、强制对齐
#pragma pack (value)时的指定对齐值value。
注意value值为1 2 4 8 16
1、确定分配单位(一行分配多少字节)
分配单位 = min(结构体中最大的基本类型, value)
2、确定成员的偏移量
成员偏移量 = 成员自身类型的整数倍。
3、收尾工作
结构体的总大小 = 分配单位整数倍

8、结构体的位域
在结构体中,以位为单位的成员,咱们称之为位段(位域)

a的类型是unsigned int a的大小 只占2位二进制位。
没有非位域隔开的位域 叫相邻位域。
相邻位域可以压缩。但是压缩的位数 不能超过自身类型的大小

不要对位域取地址。
struct A ob;
&ob.a // error对位域赋值 不要超过 位域本身位的宽度
struct A ob;
ob.a = 5;//0101 只要两位 就是01
printf("ob.a= %d\n", ob.a); //1另起一个存储单元

无意义位段:

案例

9、共用体
结构体:所有成员拥有独立空间
共用体:所有成员共享同一块空间
1 union Data
2 {
3 char a;
4 short b;
5 int c;
6 }成员a b c共享同一块空间。空间大小 由最大的成员空间决定

成员a b c共享同一块空间,但是每个成员 能操作的空间的范围 是由成员自身类型长度决定

10、枚举
枚举:将枚举变量 要赋的值 一一列举出来
enum POKER_COLOR{HONGTAO,MEIHUA,FANGKUAI,HEITAO};
阿源的知识库