[应用相关] 【转】嵌入式 C | 结构体完全笔记,收藏!

[复制链接]
5745|52
 楼主| 盗铃何须掩耳 发表于 2022-5-6 16:38 | 显示全部楼层 |阅读模式
https://mp.weixin.qq.com/s/hDMFhRurdWjnTiwbhcuS-Q


本次分享一篇关于结构体的入门、提高的笔记,**比较长,前面部分是结构体基础,已经掌握的童鞋可以跳过,直接看看后半部分的提高实例。

有的时候,我们所遇到的数据结构,不仅仅是一群数字或者是字符串那么简单。比如我们每一个人的学籍信息,学号是一个长整数,名字却是字符;甚至有更复杂的情况,这种问题在现实生活中并不少见。我们之前学过一种叫数组的数据结构,它可以允许我们把很多同类型的数据集中在一起处理。相对于之前,这已经是一次极大的进步。但是,新的问题,往往又会出现,这个时候,我们就得上更高端的装备——结构体。

相比于数组,结构体有以下的更强大的优势:

  • 批量存储数据
  • 存储不同类型的数据
  • 支持嵌套



 楼主| 盗铃何须掩耳 发表于 2022-5-6 16:40 | 显示全部楼层
结构体的声明与定义声明

结构体的声明使用struct关键字,如果我们想要把我们的学籍信息组织一下的话,可以这样表示:

  1. struct Info
  2. {
  3.     unsigned long identifier;//学号,用无符号长整数表示
  4.     char name[20];//名字,用字符数组表示
  5.     unsigned int year;//入学年份,用无符号整数表示
  6.     unsigned int years;//学制,用无符号整数表示
  7. }

这样,我们就相当于描绘好了一个框架,以后要用的话直接定义一个这种类型的变量就好了。




 楼主| 盗铃何须掩耳 发表于 2022-5-6 16:41 | 显示全部楼层
定义

我们刚刚申请了一个名叫Info的结构体类型,那么理论上我们可以像声明其他变量的操作一样,去声明我们的结构体操作,但是C语言中规定,声明结构体变量的时候,struct关键字是不可少的。

struct 结构体类型名 结构体变量名

不过,你可以在某个函数里面定义:

  1. #include <stdio.h>

  2. struct Info
  3. {
  4.     unsigned long identifier;//学号,用无符号长整数表示
  5.     char name[20];//名字,用字符数组表示
  6.     unsigned int year;//入学年份,用无符号整数表示
  7.     unsigned int years;//学制,用无符号整数表示
  8. };

  9. int main(void)
  10. {
  11.     /**
  12.      *在main函数中声明结构体变量
  13.      *结构体变量名叫info
  14.      *struct关键字不能丢
  15.      */
  16.     struct Info info;
  17.     ...
  18. }
也可以在声明的时候就把变量名定义下来(此时这个变量是全局变量):
  1. #include <stdio.h>

  2. struct Info
  3. {
  4.     unsigned long identifier;//学号,用无符号长整数表示
  5.     char name[20];//名字,用字符数组表示
  6.     unsigned int year;//入学年份,用无符号整数表示
  7.     unsigned int years;//学制,用无符号整数表示
  8. } info;
  9. /**
  10. *此时直接定义了变量
  11. *该变量是全局变量
  12. *变量名叫info
  13. */

  14. int main(void)
  15. {
  16.     ...
  17. }


 楼主| 盗铃何须掩耳 发表于 2022-5-6 16:42 | 显示全部楼层
访问结构体成员

结构体成员的访问有点不同于以往的任何变量,它是采用点号运算符.来访问成员的。比如,info.name就是引用info结构体的name成员,是一个字符数组,而info.year则可以查到入学年份,是个无符号整型。

比如,下面开始录入学生的信息:

  1. //Example 01
  2. #include <stdio.h>

  3. struct Info
  4. {
  5.     unsigned long identifier;//学号,用无符号长整数表示
  6.     char name[20];//名字,用字符数组表示
  7.     unsigned int year;//入学年份,用无符号整数表示
  8.     unsigned int years;//学制,用无符号整数表示
  9. };

  10. int main(void)
  11. {
  12.     struct Info info;

  13.     printf("请输入学生的学号:");
  14.     scanf("%d", &info.identifier);
  15.     printf("请输入学生的姓名:");
  16.     scanf("%s", info.name);
  17.     printf("请输入学生的入学年份:");
  18.     scanf("%d", &info.year);
  19.     printf("请输入学生的学制:");
  20.     scanf("%d", &info.years);

  21.     printf("\n数据录入完毕\n\n");

  22.     printf("学号:%d\n姓名:%s\n入学年份:%d\n学制:%d\n毕业时间:%d\n", \
  23.         info.identifier, info.name, info.year, info.years, info.year + info.years);
  24.     return 0;
  25. }

运行结果如下:

  1. //Consequence 01
  2. 请输入学生的学号:20191101
  3. 请输入学生的姓名:Harris
  4. 请输入学生的入学年份:2019
  5. 请输入学生的学制:4

  6. 数据录入完毕

  7. 学号:20191101
  8. 姓名:Harris
  9. 入学年份:2019
  10. 学制:4
  11. 毕业时间:2023


 楼主| 盗铃何须掩耳 发表于 2022-5-6 16:43 | 显示全部楼层
初始化结构体

像数组一样,结构体也可以在定义的时候初始化,方法也几乎一样:

  1. struct Info info = {
  2.     20191101,
  3.     "Harris",
  4.     2019,
  5.     4
  6. };

在C99标准中,还支持给指定元素赋值(就像数组一样):

  1. struct Info info = {
  2.     .name = "Harris",
  3.     .year = 2019
  4. };

对于没有被初始化的成员,则「数值型」成员初始化为0,「字符型」成员初始化为‘\0’。


 楼主| 盗铃何须掩耳 发表于 2022-5-6 16:44 | 显示全部楼层
对齐

下面这个代码,大家来看看会发生什么:

  1. //EXample 02 V1
  2. #include <stdio.h>

  3. int main(void)
  4. {
  5.     struct A
  6.     {
  7.         char a;
  8.         int b;
  9.         char c;
  10.     } a = {'a', 10, 'o'};
  11.    
  12.     printf("size of a = %d\n", sizeof(a));
  13.    
  14.     return 0;
  15. }

我们之前学过,char类型的变量占1字节,int类型的变量占4字节,那么这么一算,一个结构体A型的变量应该就是6字节了。别急,我们看运行结果:

  1. //COnsequence 02 V1
  2. size of a = 12

怎么变成12了呢?标准更新了?老师教错了?都不是。我们把代码改一下:

  1. //EXample 02 V2
  2. #include <stdio.h>

  3. int main(void)
  4. {
  5.     struct A
  6.     {
  7.         char a;
  8.         char c;
  9.         int b;
  10.     } a = {'a', 'o', 10};
  11.    
  12.     printf("size of a = %d\n", sizeof(a));
  13.    
  14.     return 0;
  15. }

结果:

  1. //Consequence 02 V2
  2. size of a = 8

实际上,这是编译器对我们程序的一种优化——内存对齐。在第一个例子中,第一个和第三个成员是char类型是1个字节,而中间的int却有4个字节,为了对齐,两个char也占用了4个字节,于是就是12个字节。

而在第二个例子里面,前两个都是char,最后一个是int,那么前两个可以一起占用4个字节(实际只用2个,第一个例子也同理,只是为了访问速度更快,而不是为了扩展),最后的int占用4字节,合起来就是8个字节。

关于如何声明结构体来节省内存容量,可以阅读下面的这篇**,作者是艾瑞克·雷蒙,时尚最具争议性的黑客之一,被公认为开源运动的主要领导者之一:

英文原版,中文版



 楼主| 盗铃何须掩耳 发表于 2022-5-6 16:46 | 显示全部楼层
结构体嵌套

在学籍里面,如果我们的日期想要更加详细一些,精确到day,这时候就可以使用结构体嵌套来完成:

  1. #include <stdio.h>

  2. struct Date
  3. {
  4.     unsigned int year;
  5.     unsigned int month;
  6.     unsigned int day;
  7. };

  8. struct Info
  9. {
  10.     unsigned long identifier;//学号,用无符号长整数表示
  11.     char name[20];//名字,用字符数组表示
  12.     struct Date date;/*---入学日期,用结构体Date表示---*/
  13.     unsigned int years;//学制,用无符号整数表示
  14. };

  15. int main(void)
  16. {
  17.     ...
  18. }

如此一来,比我们单独声明普通变量快多了。

不过,这样访问变量,就必须用点号一层层往下访问。比如要访问day这个成员,那就只能info.date.day而不能直接info.date或者info,day。

  1. //Example 03
  2. #include <stdio.h>

  3. struct Date
  4. {
  5.     unsigned int year;
  6.     unsigned int month;
  7.     unsigned int day;
  8. };

  9. struct Info
  10. {
  11.     unsigned long identifier;//学号,用无符号长整数表示
  12.     char name[20];//名字,用字符数组表示
  13.     struct Date date;/*---入学日期,用结构体Date表示---*/
  14.     unsigned int years;//学制,用无符号整数表示
  15. };

  16. int main(void)
  17. {
  18.     struct Info info;
  19.     printf("请输入学生的学号:");
  20.     scanf("%d", &info.identifier);
  21.     printf("请输入学生的姓名:");
  22.     scanf("%s", info.name);
  23.     printf("请输入学生的入学年份:");
  24.     scanf("%d", &info.date.year);
  25.     printf("请输入学生的入学月份:");
  26.     scanf("%d", &info.date.month);
  27.     printf("请输入学生的入学日期:");
  28.     scanf("%d", &info.date.day);
  29.     printf("请输入学生的学制:");
  30.     scanf("%d", &info.years);

  31.     printf("\n数据录入完毕\n\n");

  32.     printf("学号:%d\n姓名:%s\n入学时间:%d/%d/%d\n学制:%d\n毕业时间:%d\n",\
  33.            info.identifier, info.name,\
  34.            info.date.year, info.date.month, info.date.day,\
  35.            info.years, info.date.year + info.years);
  36.     return 0;
  37. }
  38. 运行结果如下:

  39. //Consequence 03
  40. 请输入学生的学号:20191101
  41. 请输入学生的姓名:Harris
  42. 请输入学生的入学年份:2019
  43. 请输入学生的入学月份:9
  44. 请输入学生的入学日期:7
  45. 请输入学生的学制:4

  46. 数据录入完毕

  47. 学号:20191101
  48. 姓名:Harris
  49. 入学时间:2019/9/7
  50. 学制:4
  51. 毕业时间:2023



 楼主| 盗铃何须掩耳 发表于 2022-5-6 16:47 | 显示全部楼层
结构体数组

刚刚我们演示了存储一个学生的学籍信息的时候,使用结构体的例子。那么,如果要录入一批学生,这时候我们就可以沿用之前的思路,使用结构体数组。

我们知道,数组的定义,就是存放一堆相同类型的数据的容器。而结构体一旦被我们声明,那么你就可以把它看作一个类型,只不过是你自己定义的罢了。

定义结构体数组也很简单:

  1. struct 结构体类型
  2. {
  3.     成员;
  4. } 数组名[长度];

  5. /****或者这样****/

  6. struct 结构体类型
  7. {
  8.     成员;
  9. };
  10. struct 结构体类型 数组名[长度];


 楼主| 盗铃何须掩耳 发表于 2022-5-6 16:48 | 显示全部楼层
结构体指针

既然我们可以把结构体看作一个类型,那么也就必然有对应的指针变量。

  1. struct Info* pinfo;

但是在指针这里,结构体和数组就不一样了。我们知道,数组名实际上就是指向这个数组第一个元素的地址,所以可以将数组名直接赋值给指针。而结构体的变量名并不是指向该结构体的地址,所以要使用取地址运算符&才能获取地址:

  1. pinfo = &info;

通过结构体指针来访问结构体有以下两种方法:

  • (*结构体指针).成员名
  • 结构体指针->成员名

第一个方法由于点号运算符比指针的取值运算符优先级更高,因此需要加一个小括号来确定优先级,让指针先解引用变成结构体变量,在使用点号的方法去访问。

相比之下,第二种方法就直观许多。

这两种方法在实现上是完全等价的,但是点号只能用于结构体变量,而箭头只能够用于指针。

第一种方法:

  1. #include <stdio.h>
  2. ...
  3. int main(void)
  4. {
  5.     struct Info *p;
  6.     p = &info;
  7.    
  8.     printf("学号:\n", (*p).identifier);
  9.     printf("姓名:\n", (*p).name);
  10.     printf("入学时间:%d/%d/%d\n", (*p).date.year, (*p).date.month, (*p).date.day);
  11.     printf("学制:\n", (*p).years);
  12.     return 0;
  13. }

第二种方法:

  1. #include <stdio.h>
  2. ...
  3. int main(void)
  4. {
  5.     struct Info *p;
  6.     p = &info;
  7.    
  8.     printf("学号:\n", p -> identifier);
  9.     printf("姓名:\n", p -> name);
  10.     printf("入学时间:%d/%d/%d\n", p -> date.year, p -> date.month, p -> date.day);
  11.     printf("学制:\n", p -> years);
  12.     return 0;
  13. }


 楼主| 盗铃何须掩耳 发表于 2022-5-6 16:49 | 显示全部楼层
传递结构体信息传递结构体变量

我们先来看看下面的代码:

  1. //Example 04
  2. #include <stdio.h>

  3. int main(void)
  4. {
  5.     struct Test
  6.     {
  7.         int x;
  8.         int y;
  9.     }t1, t2;

  10.     t1.x = 3;
  11.     t1.y = 4;
  12.     t2 = t1;

  13.     printf("t2.x = %d, t2.y = %d\n", t2.x, t2.y);
  14.     return 0;
  15. }
运行结果如下:
  1. //Consequence 04
  2. t2.x = 3, t2.y = 4

这么看来,结构体是可以直接赋值的。那么既然这样,作为函数的参数和返回值也自然是没问题的了。

先来试试作为参数:

  1. //Example 05
  2. #include <stdio.h>
  3. struct Date
  4. {
  5.     unsigned int year;
  6.     unsigned int month;
  7.     unsigned int day;
  8. };

  9. struct Info
  10. {
  11.     unsigned long identifier;
  12.     char name[20];
  13.     struct Date date;
  14.     unsigned int years;
  15. };

  16. struct Info getInput(struct Info info);
  17. void printInfo(struct Info info);

  18. struct Info getInput(struct Info info)
  19. {
  20.     printf("请输入学号:");
  21.     scanf("%d", &info.identifier);
  22.     printf("请输入姓名:");
  23.     scanf("%s", info.name);
  24.     printf("请输入入学年份:");
  25.     scanf("%d", &info.date.year);
  26.     printf("请输入月份:");
  27.     scanf("%d", &info.date.month);
  28.     printf("请输入日期:");
  29.     scanf("%d", &info.date.day);
  30.     printf("请输入学制:");
  31.     scanf("%d", &info.years);

  32.     return info;
  33. }

  34. void printInfo(struct Info info)
  35. {
  36.     printf("学号:%d\n姓名:%s\n入学时间:%d/%d/%d\n学制:%d\n毕业时间:%d\n", \
  37.         info.identifier, info.name, \
  38.         info.date.year, info.date.month, info.date.day, \
  39.         info.years, info.date.year + info.years);
  40. }

  41. int main(void)
  42. {
  43.     struct Info i1 = {};
  44.     struct Info i2 = {};
  45.     printf("请录入第一个同学的信息...\n");
  46.     i1 = getInput(i1);
  47.     putchar('\n');
  48.     printf("请录入第二个学生的信息...\n");
  49.     i2 = getInput(i2);

  50.     printf("\n录入完毕,现在开始打印...\n\n");
  51.     printf("打印第一个学生的信息...\n");
  52.     printInfo(i1);
  53.     putchar('\n');
  54.     printf("打印第二个学生的信息...\n");
  55.     printInfo(i2);

  56.     return 0;
  57. }

运行结果如下:

  1. //Consequence 05
  2. 请录入第一个同学的信息...
  3. 请输入学号:20191101
  4. 请输入姓名:Harris
  5. 请输入入学年份:2019
  6. 请输入月份:9
  7. 请输入日期:7
  8. 请输入学制:4

  9. 请录入第二个学生的信息...
  10. 请输入学号:20191102
  11. 请输入姓名:Joy
  12. 请输入入学年份:2019
  13. 请输入月份:9
  14. 请输入日期:8
  15. 请输入学制:5

  16. 录入完毕,现在开始打印...

  17. 打印第一个学生的信息...
  18. 学号:20191101
  19. 姓名:Harris
  20. 入学时间:2019/9/7
  21. 学制:4
  22. 毕业时间:2023

  23. 打印第二个学生的信息...
  24. 学号:20191102
  25. 姓名:Joy
  26. 入学时间:2019/9/8
  27. 学制:5
  28. 毕业时间:2024


 楼主| 盗铃何须掩耳 发表于 2022-5-6 16:50 | 显示全部楼层
传递指向结构体变量的指针

早期的C语言是不允许直接将结构体作为参数直接传递进去的。主要是考虑到如果结构体的内存占用太大,那么整个程序的内存开销就会爆炸。不过现在的C语言已经放开了这方面的限制。

不过,作为一名合格的开发者,我们应该要去珍惜硬件资源。那么,传递指针就是一个很好的办法。

将刚才的代码修改一下:

  1. //Example 06
  2. #include <stdio.h>
  3. struct Date
  4. {
  5.     unsigned int year;
  6.     unsigned int month;
  7.     unsigned int day;
  8. };

  9. struct Info
  10. {
  11.     unsigned long identifier;
  12.     char name[20];
  13.     struct Date date;
  14.     unsigned int years;
  15. };

  16. void getInput(struct Info *info);
  17. void printInfo(struct Info *info);

  18. void getInput(struct Info *info)
  19. {
  20.     printf("请输入学号:");
  21.     scanf("%d", &info->identifier);
  22.     printf("请输入姓名:");
  23.     scanf("%s", info->name);
  24.     printf("请输入入学年份:");
  25.     scanf("%d", &info->date.year);
  26.     printf("请输入月份:");
  27.     scanf("%d", &info->date.month);
  28.     printf("请输入日期:");
  29.     scanf("%d", &info->date.day);
  30.     printf("请输入学制:");
  31.     scanf("%d", &info->years);
  32. }

  33. void printInfo(struct Info *info)
  34. {
  35.     printf("学号:%d\n姓名:%s\n入学时间:%d/%d/%d\n学制:%d\n毕业时间:%d\n", \
  36.         info->identifier, info->name, \
  37.         info->date.year, info->date.month, info->date.day, \
  38.         info->years, info->date.year + info->years);
  39. }

  40. int main(void)
  41. {
  42.     struct Info i1 = {};
  43.     struct Info i2 = {};
  44.     printf("请录入第一个同学的信息...\n");
  45.     getInput(&i1);
  46.     putchar('\n');
  47.     printf("请录入第二个学生的信息...\n");
  48.     getInput(&i2);

  49.     printf("\n录入完毕,现在开始打印...\n\n");
  50.     printf("打印第一个学生的信息...\n");
  51.     printInfo(&i1);
  52.     putchar('\n');
  53.     printf("打印第二个学生的信息...\n");
  54.     printInfo(&i2);

  55.     return 0;
  56. }

此时传递的就是一个指针,而不是一个庞大的结构体。


 楼主| 盗铃何须掩耳 发表于 2022-5-6 16:51 | 显示全部楼层
动态申请结构体

结构体也可以在堆里面动态申请:

  1. //Example 01
  2. #include <stdio.h>
  3. ...
  4. int main(void)
  5. {
  6.     struct Info *i1;
  7.     struct Info *i2;
  8.    
  9.     i1 = (struct Info *)malloc(sizeof(struct Info));
  10.     i2 = (struct Info *)malloc(sizeof(struct Info));
  11.     if (i1 == NULL || i2 == NULL)
  12.     {
  13.         printf("内存分配失败!\n");
  14.         exit(1);
  15.     }
  16.    
  17.     printf("请录入第一个同学的信息...\n");
  18.     getInput(i1);
  19.     putchar('\n');
  20.     printf("请录入第二个学生的信息...\n");
  21.     getInput(i2);

  22.     printf("\n录入完毕,现在开始打印...\n\n");
  23.     printf("打印第一个学生的信息...\n");
  24.     printInfo(i1);
  25.     putchar('\n');
  26.     printf("打印第二个学生的信息...\n");
  27.     printInfo(i2);
  28.    
  29.     free(i1);
  30.     free(i2);
  31.    
  32.     return 0;
  33. }


 楼主| 盗铃何须掩耳 发表于 2022-5-6 16:53 | 显示全部楼层
实战:建立一个图书馆数据库

实际上,我们建立的数组可以是指向结构体指针的数组。

代码实现如下:

  1. //Example 02
  2. #include <stdio.h>
  3. #include <stdlib.h>

  4. #define MAX_SIZE 100

  5. struct Date
  6. {
  7.     int year;
  8.     int month;
  9.     int day;
  10. };

  11. struct Book
  12. {
  13.     char title[128];
  14.     char author[48];
  15.     float price;
  16.     struct Date date;
  17.     char publisher[48];
  18. };

  19. void getInput(struct Book* book);//录入数据
  20. void printBook(struct Book* book);//打印数据
  21. void initLibrary(struct Book* lib[]);//初始化结构体
  22. void printLibrary(struct Book* lib[]);//打印单本书数据
  23. void releaseLibrary(struct Book* lib[]);//释放内存

  24. void getInput(struct Book* book)
  25. {
  26.     printf("请输入书名:");
  27.     scanf("%s", book->title);
  28.     printf("请输入作者:");
  29.     scanf("%s", book->author);
  30.     printf("请输入售价:");
  31.     scanf("%f", &book->price);
  32.     printf("请输入出版日期:");
  33.     scanf("%d-%d-%d", &book->date.year, &book->date.month, &book->date.day);
  34.     printf("请输入出版社:");
  35.     scanf("%s", book->publisher);
  36. }

  37. void printBook(struct Book* book)
  38. {
  39.     printf("书名:%s\n", book->title);
  40.     printf("作者:%s\n", book->author);
  41.     printf("售价:%.2f\n", book->price);
  42.     printf("出版日期:%d-%d-%d\n", book->date.year, book->date.month, book->date.day);
  43.     printf("出版社:%s\n", book->publisher);
  44. }

  45. void initLibrary(struct Book* lib[])
  46. {
  47.     for (int i = 0; i < MAX_SIZE; i++)
  48.     {
  49.         lib[i] = NULL;
  50.     }
  51. }

  52. void printLibrary(struct Book* lib[])
  53. {
  54.     for (int i = 0; i < MAX_SIZE; i++)
  55.     {
  56.         if (lib[i] != NULL)
  57.         {
  58.             printBook(lib[i]);
  59.             putchar('\n');
  60.         }
  61.     }
  62. }

  63. void releaseLibrary(struct Book* lib[])
  64. {
  65.     for (int i = 0; i < MAX_SIZE; i++)
  66.     {
  67.         if (lib[i] != NULL)
  68.         {
  69.             free(lib[i]);
  70.         }
  71.     }
  72. }

  73. int main(void)
  74. {
  75.     struct Book* lib[MAX_SIZE];
  76.     struct Book* p = NULL;
  77.     int ch, index = 0;

  78.     initLibrary(lib);

  79.     while (1)
  80.     {
  81.         printf("请问是否要录入图书信息(Y/N):");
  82.         do
  83.         {
  84.             ch = getchar();
  85.         } while (ch != 'Y' && ch != 'N');

  86.         if (ch == 'Y')
  87.         {
  88.             if (index < MAX_SIZE)
  89.             {
  90.                 p = (struct Book*)malloc(sizeof(struct Book));
  91.                 getInput(p);
  92.                 lib[index] = p;
  93.                 index++;
  94.                 putchar('\n');
  95.             }
  96.             else
  97.             {
  98.                 printf("数据库已满!\n");
  99.                 break;
  100.             }
  101.         }
  102.         else
  103.         {
  104.             break;
  105.         }
  106.     }

  107.     printf("\n数据录入完毕,开始打印验证...\n\n");
  108.     printLibrary(lib);
  109.     releaseLibrary(lib);

  110.     return 0;
  111. }
运行结果如下:
  1. //Consequence 02
  2. 请问是否要录入图书信息(Y/N):Y
  3. 请输入书名:人类简史
  4. 请输入作者:尤瓦尔·赫拉利
  5. 请输入售价:32.25
  6. 请输入出版日期:2016-3-4
  7. 请输入出版社:中信出版集团

  8. 请问是否要录入图书信息(Y/N):N

  9. 数据录入完毕,开始打印验证...

  10. 书名:人类简史
  11. 作者:尤瓦尔·赫拉利
  12. 售价:32.25
  13. 出版日期:2016-3-4
  14. 出版社:中信出版集团


 楼主| 盗铃何须掩耳 发表于 2022-5-6 16:54 | 显示全部楼层
单链表

我们知道,数组变量在内存中,是连续的,而且不可拓展。显然在一些情况下,这种数据结构拥有很大的局限性。比如移动数据的时候,会牵一发而动全身,尤其是反转这种操作更加令人窒息。那么,需要需要一种数据结构来弄出一种更加灵活的“数组”,那么这,就是「链表」

本节我们只讲讲单链表。

所谓链表,就是由一个个「结点」组成的一个数据结构。每个结点都有「数据域」「指针域」组成。其中数据域用来存储你想要存储的信息,而指针域用来存储下一个结点的地址。如图:

924206274e219e48e2.png
单链表

当然,链表最前面还有一个头指针,用来存储头结点的地址。

这样一来,链表中的每一个结点都可以不用挨个存放,因为有了指针把他们串起来。因此结点放在哪都无所谓,反正指针总是能够指向下一个元素。我们只需要知道头指针,就能够顺藤摸瓜地找到整个链表。

因此对于学籍数据库来说,我们只需要在Info结构体中加上一个指向自身类型的成员即可:

  1. struct Info
  2. {
  3.     unsigned long identifier;
  4.     char name[20];
  5.     struct Date date;
  6.     unsigned int years;
  7.     struct Info* next;
  8. };





 楼主| 盗铃何须掩耳 发表于 2022-5-6 16:56 | 显示全部楼层
在单链表中插入元素头插法

这种每次都将数据插入单链表的头部(头指针后面)的插入法就叫头插法。

如果要把学生信息加入到单链表,可以这么写:

  1. void addInfo(struct Info** students)//students是头指针
  2. {
  3.     struct Info* info, *temp;
  4.     info = (struct Info*)malloc(sizeof(struct Info));
  5.     if (info == NULL)
  6.     {
  7.         printf("内存分配失败!\n");
  8.         exit(1);
  9.     }
  10.    
  11.     getInput(info);
  12.    
  13.     if (*students != NULL)
  14.     {
  15.         temp = *students;
  16.         *students = info;
  17.         info->next = temp;
  18.     }
  19.     else
  20.     {
  21.         *students = info;
  22.         info->next = NULL;
  23.     }
  24. }
[color=rgba(127, 194, 194, 0.498)]

由于students存放的是头指针,因此我们需要传入它的地址传递给函数,才能够改变它本身的值。而students本身又是一个指向Info结构体的指针,所以参数的类型应该就是struct Info**。

[color=rgba(127, 194, 194, 0.498)]❞

往单链表里面添加一个结点,也就是先申请一个结点,然后判断链表是否为空。如果为空,那么直接将头指针指向它,然后next成员指向NULL。若不为空,那么先将next指向头指针原本指向的结点,然后将头指针指向新结点即可。

那么,打印链表也变得很简单:

  1. void printStu(struct Info* students)
  2. {
  3.     struct Info* info;
  4.     int count = 1;
  5.    
  6.     info = students;
  7.     while (book != NULL)
  8.     {
  9.         printf("Student%d:\n", count);
  10.         printf("姓名:%s\n", info->name);
  11.         printf("学号:%d\n", info->identifier);
  12.         info = info->next;
  13.         count++;
  14.     }
  15. }

想要读取单链表里面的数据,只需要迭代单链表中的每一个结点,直到next成员为NULL,即表示单链表的结束。

最后,当然还是别忘了释放空间:

  1. void releaseStu(struct Info** students)
  2. {
  3.     struct Info* temp;
  4.    
  5.     while (*students != NULL)
  6.     {
  7.         temp = *students;
  8.         *students = (*students)->next;
  9.         free(temp);
  10.     }
  11. }
尾插法

与头插法类似,尾插法就是把每一个数据都插入到链表的末尾。

  1. void addInfo(struct Info** students)
  2. {
  3.     struct Info* info, *temp;
  4.     info = (struct Info*)malloc(sizeof(struct Info));
  5.     if (info == NULL)
  6.     {
  7.         printf("内存分配失败!\n");
  8.         exit(1);
  9.     }
  10.    
  11.     getInput(info);
  12.    
  13.     if (*students != NULL)
  14.     {
  15.         temp = *students;
  16.         *students = info;
  17.         //定位到链表的末尾的位置
  18.         while (temp->next != NULL)
  19.         {
  20.             temp = temp->next;
  21.         }
  22.         //插入数据
  23.         temp->next = info;
  24.         info->next = temp;
  25.     }
  26.     else
  27.     {
  28.         *students = info;
  29.         info->next = NULL;
  30.     }
  31. }

这么一来,程序执行的效率难免要降低很多,因为每次插入数据,都要先遍历一次链表。如果链表很长,那么对于插入数据来说就是一次灾难。不过,我们可以给程序添加一个指针,让它永远都指向链表的尾部,这样一来,就可以用很少的空间换取很高的程序执行效率。

代码更改如下:

  1. void addInfo(struct Info** students)
  2. {
  3.     struct Info* info, *temp;
  4.     static struct Info* tail;//设置静态指针
  5.     info = (struct Info*)malloc(sizeof(struct Info));
  6.     if (info == NULL)
  7.     {
  8.         printf("内存分配失败!\n");
  9.         exit(1);
  10.     }
  11.    
  12.     getInput(info);
  13.    
  14.     if (*students != NULL)
  15.     {
  16.         tail->next = info;
  17.         info->next = NULL;
  18.     }
  19.     else
  20.     {
  21.         *students = info;
  22.         info->next = NULL;
  23.     }
  24. }


 楼主| 盗铃何须掩耳 发表于 2022-5-6 16:57 | 显示全部楼层
搜索单链表

单链表是我们用来存储数据的一个容器,那么有时候需要快速查找信息就需要开发相关搜索的功能。比如说输入学号,查找同学的所有信息。

  1. struct Info *searchInfo(struct Info* students, long* target)
  2. {
  3.     struct Info* info;
  4.     info = students;
  5.     while (info != NULL)
  6.     {
  7.         if (info->identifier == target)
  8.         {
  9.             break;
  10.         }
  11.         info = info->next;
  12.     }
  13.    
  14.     return book;
  15. };

  16. void printInfo(struct Info* info)
  17. {
  18.     ...
  19. }
  20. ...

  21. int main(void)
  22. {
  23.     ...
  24.     printf("\n请输入学生学号:");
  25.     scanf("%d", input);
  26.     info = searchInfo(students, input);
  27.     if (info == NULL)
  28.     {
  29.         printf("抱歉,未找到相关结果!\n");
  30.     }
  31.     else
  32.     {
  33.         do
  34.         {
  35.             printf("相关结果如下:\n");
  36.             printInfo(book);
  37.         } while ((info = searchInfo(info->next, input)) != NULL);
  38.     }
  39.    
  40.     releaseInfo(...);
  41.     return 0;
  42. }


 楼主| 盗铃何须掩耳 发表于 2022-5-6 16:58 | 显示全部楼层
插入结点到指定位置

到了这里,才体现出链表真正的优势。

设想一下,如果有一个有序数组,现在要求你去插入一个数字,插入完成之后,数组依然保持有序。你会怎么做?

没错,你应该会挨个去比较,然后找到合适的位置(当然这里也可以使用二分法,比较节省算力),把这个位置后面的所有数都往后移动一个位置,然后将我们要插入的数字放入刚刚我们腾出来的空间里面。

你会发现,这样的处理方法,经常需要移动大量的数据,对于程序的执行效率来说,是一个不利因素。那么链表,就无所谓。反正在内存中,链表的存储毫无逻辑,我们只需要改变指针的值就可以实现链表的中间插入。


  1. //Example 03
  2. #include <stdio.h>
  3. #include <stdlib.h>

  4. struct Node
  5. {
  6.     int value;
  7.     struct Node* next;
  8. };

  9. void insNode(struct Node** head, int value)
  10. {
  11.     struct Node* pre;
  12.     struct Node* cur;
  13.     struct Node* New;

  14.     cur = *head;
  15.     pre = NULL;

  16.     while (cur != NULL && cur->value < value)
  17.     {
  18.         pre = cur;
  19.         cur = cur->next;
  20.     }

  21.     New = (struct Node*)malloc(sizeof(struct Node));
  22.     if (New == NULL)
  23.     {
  24.         printf("内存分配失败!\n");
  25.         exit(1);
  26.     }
  27.     New->value = value;
  28.     New->next = cur;

  29.     if (pre == NULL)
  30.     {
  31.         *head = New;
  32.     }
  33.     else
  34.     {
  35.         pre->next = New;
  36.     }
  37. }

  38. void printNode(struct Node* head)
  39. {
  40.     struct Node* cur;

  41.     cur = head;
  42.     while (cur != NULL)
  43.     {
  44.         printf("%d ", cur->value);
  45.         cur = cur->next;
  46.     }
  47.     putchar('\n');
  48. }

  49. int main(void)
  50. {
  51.     struct Node* head = NULL;
  52.     int input;

  53.     printf("开始插入整数...\n");
  54.     while (1)
  55.     {
  56.         printf("请输入一个整数,输入-1表示结束:");
  57.         scanf("%d", &input);
  58.         if (input == -1)
  59.         {
  60.             break;
  61.         }
  62.         insNode(&head, input);
  63.         printNode(head);
  64.     }

  65.     return 0;
  66. }
运行结果如下:
  1. //Consequence 03
  2. 开始插入整数...
  3. 请输入一个整数,输入-1表示结束:4
  4. 4
  5. 请输入一个整数,输入-1表示结束:5
  6. 4 5
  7. 请输入一个整数,输入-1表示结束:3
  8. 3 4 5
  9. 请输入一个整数,输入-1表示结束:6
  10. 3 4 5 6
  11. 请输入一个整数,输入-1表示结束:2
  12. 2 3 4 5 6
  13. 请输入一个整数,输入-1表示结束:5
  14. 2 3 4 5 5 6
  15. 请输入一个整数,输入-1表示结束:1
  16. 1 2 3 4 5 5 6
  17. 请输入一个整数,输入-1表示结束:7
  18. 1 2 3 4 5 5 6 7
  19. 请输入一个整数,输入-1表示结束:-1



 楼主| 盗铃何须掩耳 发表于 2022-5-6 16:59 | 显示全部楼层
删除结点

删除结点的思路也差不多,首先修改待删除的结点的上一个结点的指针,将其指向待删除结点的下一个结点。然后释放待删除结点的空间。

  1. ...
  2. void delNode(struct Node** head, int value)
  3. {
  4.     struct Node* pre;
  5.     struct Node* cur;
  6.    
  7.     cur = *head;
  8.     pre = NULL;
  9.     while (cur != NULL && cur->value != value)
  10.     {
  11.         pre = cur;
  12.         cur = cur->next;
  13.     }
  14.     if (cur == NULL)
  15.     {
  16.         printf("未找到匹配项!\n");
  17.         return ;
  18.     }
  19.     else
  20.     {
  21.         if (pre == NULL)
  22.         {
  23.             *head = cur->next;
  24.         }
  25.         else
  26.         {
  27.             pre->next = cur->next;
  28.         }
  29.         free(cur);
  30.     }
  31. }



 楼主| 盗铃何须掩耳 发表于 2022-5-6 17:01 | 显示全部楼层


内存池

C语言的内存管理,从来都是一个让人头秃的问题。要想更自由地管理内存,就必须去堆中申请,然后还需要考虑何时释放,万一释放不当,或者没有及时释放,造成的后果都是难以估量的。

当然如果就这些,那倒也还不算什么。问题就在于,如果大量地使用malloc和free函数来申请内存,首先使要经历一个从应用层切入系统内核层,调用完成之后,再返回应用层的一系列步骤,实际上使非常浪费时间的。更重要的是,还会产生大量的内存碎片。比如,先申请了一个1KB的空间,紧接着又申请了一个8KB的空间。而后,这个1KB使用完了,被释放,但是这个空间却只有等到下一次有刚好1KB的空间申请,才能够被重新调用。这么一来,极限情况下,整个堆有可能被弄得支离破碎,最终导致大量内存浪费。

那么这种情况下,我们解决这类问题的思路,就是创建一个内存池。

内存池,实际上就是我们让程序创建出来的一块额外的缓存区域,如果有需要释放内存,先不必使用free函数,如果内存池有空,那么直接放入内存池。同样的道理,下一次程序申请空间的时候,先检查下内存池里面有没有合适的内存,如果有,则直接拿出来调用,如果没有,那么再使用malloc。

其实内存池我们就可以使用单链表来进行维护,下面通过一个通讯录的程序来说明内存池的运用。

普通的版本:

  1. //Example 04 V1
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>

  5. struct Person
  6. {
  7. char name[40];
  8. char phone[20];
  9. struct Person* next;
  10. };

  11. void getInput(struct Person* person);
  12. void printPerson(struct Person* person);
  13. void addPerson(struct Person** contects);
  14. void changePerson(struct Person* contacts);
  15. void delPerson(struct Person** contacts);
  16. struct Person* findPerson(struct Person* contacts);
  17. void displayContacts(struct Person* contacts);
  18. void releaseContacts(struct Person** contacts);

  19. void getInput(struct Person* person)
  20. {
  21. printf("请输入姓名:");
  22. scanf("%s", person->name);
  23. printf("请输入电话:");
  24. scanf("%s", person->phone);
  25. }

  26. void addPerson(struct Person** contacts)
  27. {
  28. struct Person* person;
  29. struct Person* temp;

  30. person = (struct Person*)malloc(sizeof(struct Person));
  31. if (person == NULL)
  32. {
  33.   printf("内存分配失败!\n");
  34.   exit(1);
  35. }

  36. getInput(person);

  37. //将person添加到通讯录中
  38. if (*contacts != NULL)
  39. {
  40.   temp = *contacts;
  41.   *contacts = person;
  42.   person->next = temp;
  43. }
  44. else
  45. {
  46.   *contacts = person;
  47.   person->next = NULL;
  48. }
  49. }

  50. void printPerson(struct Person* person)
  51. {
  52. printf("联系人:%s\n", person->name);
  53. printf("电话:%s\n", person->phone);
  54. }

  55. struct Person* findPerson(struct Person* contacts)
  56. {
  57. struct Person* current;
  58. char input[40];

  59. printf("请输入联系人:");
  60. scanf("%s", input);

  61. current = contacts;
  62. while (current != NULL && strcmp(current->name, input))
  63. {
  64.   current = current->next;
  65. }

  66. return current;
  67. }

  68. void changePerson(struct Person* contacts)
  69. {
  70. struct Person* person;

  71. person = findPerson(contacts);
  72. if (person == NULL)
  73. {
  74.   printf("找不到联系人!\n");
  75. }
  76. else
  77. {
  78.   printf("请输入联系电话:");
  79.   scanf("%s", person->phone);
  80. }
  81. }

  82. void delPerson(struct Person** contacts)
  83. {
  84. struct Person* person;
  85. struct Person* current;
  86. struct Person* previous;

  87. //先找到待删除的节点的指针
  88. person = findPerson(*contacts);
  89. if (person == NULL)
  90. {
  91.   printf("找不到该联系人!\n");
  92. }
  93. else
  94. {
  95.   current = *contacts;
  96.   previous = NULL;

  97.   //将current定位到待删除的节点
  98.   while (current != NULL && current != person)
  99.   {
  100.    previous = current;
  101.    current = current->next;
  102.   }

  103.   if (previous == NULL)
  104.   {
  105.    //若待删除的是第一个节点
  106.    *contacts = current->next;
  107.   }
  108.   else
  109.   {
  110.    //若待删除的不是第一个节点
  111.    previous->next = current->next;
  112.   }

  113.   free(person);//将内存空间释放
  114. }
  115. }

  116. void displayContacts(struct Person* contacts)
  117. {
  118. struct Person* current;

  119. current = contacts;
  120. while (current != NULL)
  121. {
  122.   printPerson(current);
  123.   current = current->next;
  124. }
  125. }

  126. void releaseContacts(struct Person** contacts)
  127. {
  128. struct Person* temp;

  129. while (*contacts != NULL)
  130. {
  131.   temp = *contacts;
  132.   *contacts = (*contacts)->next;
  133.   free(temp);
  134. }
  135. }

  136. int main(void)
  137. {
  138. int code;
  139. struct Person* contacts = NULL;
  140. struct Person* person;

  141. printf("| 欢迎使用通讯录管理程序 |\n");
  142. printf("|--- 1:插入新的联系人 ---|\n");
  143. printf("|--- 2:查找现有联系人 ---|\n");
  144. printf("|--- 3:更改现有联系人 ---|\n");
  145. printf("|--- 4:删除现有联系人 ---|\n");
  146. printf("|--- 5:显示当前通讯录 ---|\n");
  147. printf("|--- 6:退出通讯录程序 ---|\n");

  148. while (1)
  149. {
  150.   printf("\n请输入指令代码:");
  151.   scanf("%d", &code);
  152.   switch (code)
  153.   {
  154.   case 1:addPerson(&contacts); break;
  155.   case 2:person = findPerson(contacts);
  156.    if (person == NULL)
  157.    {
  158.     printf("找不到该联系人!\n");
  159.    }
  160.    else
  161.    {
  162.     printPerson(person);
  163.    }
  164.    break;
  165.   case 3:changePerson(contacts); break;
  166.   case 4:delPerson(&contacts); break;
  167.   case 5:displayContacts(contacts); break;
  168.   case 6:goto END;
  169.   }
  170. }

  171. END://此处直接跳出恒循环
  172. releaseContacts(&contacts);

  173. return 0;

  174. }

运行结果如下:

  1. //Consequence 04 V1
  2. | 欢迎使用通讯录管理程序 |
  3. |--- 1:插入新的联系人 ---|
  4. |--- 2:查找现有联系人 ---|
  5. |--- 3:更改现有联系人 ---|
  6. |--- 4:删除现有联系人 ---|
  7. |--- 5:显示当前通讯录 ---|
  8. |--- 6:退出通讯录程序 ---|

  9. 请输入指令代码:1
  10. 请输入姓名:HarrisWilde
  11. 请输入电话:0101111

  12. 请输入指令代码:1
  13. 请输入姓名:Jack
  14. 请输入电话:0101112

  15. 请输入指令代码:1
  16. 请输入姓名:Rose
  17. 请输入电话:0101113

  18. 请输入指令代码:2
  19. 请输入联系人:HarrisWilde
  20. 联系人:HarrisWilde
  21. 电话:0101111

  22. 请输入指令代码:2
  23. 请输入联系人:Mike
  24. 找不到该联系人!

  25. 请输入指令代码:5
  26. 联系人:Rose
  27. 电话:0101113
  28. 联系人:Jack
  29. 电话:0101112
  30. 联系人:HarrisWilde
  31. 电话:0101111

  32. 请输入指令代码:3
  33. 请输入联系人:HarrisWilde
  34. 请输入联系电话:0101234

  35. 请输入指令代码:5
  36. 联系人:Rose
  37. 电话:0101113
  38. 联系人:Jack
  39. 电话:0101112
  40. 联系人:HarrisWilde
  41. 电话:0101234

  42. 请输入指令代码:6
下面加入内存池:
  1. //Example 04 V2
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>

  5. #define MAX 1024

  6. struct Person
  7. {
  8. char name[40];
  9. char phone[20];
  10. struct Person* next;
  11. };

  12. struct Person* pool = NULL;
  13. int count;

  14. void getInput(struct Person* person);
  15. void printPerson(struct Person* person);
  16. void addPerson(struct Person** contects);
  17. void changePerson(struct Person* contacts);
  18. void delPerson(struct Person** contacts);
  19. struct Person* findPerson(struct Person* contacts);
  20. void displayContacts(struct Person* contacts);
  21. void releaseContacts(struct Person** contacts);
  22. void releasePool(void);

  23. void getInput(struct Person* person)
  24. {
  25. printf("请输入姓名:");
  26. scanf("%s", person->name);
  27. printf("请输入电话:");
  28. scanf("%s", person->phone);
  29. }

  30. void addPerson(struct Person** contacts)
  31. {
  32. struct Person* person;
  33. struct Person* temp;

  34. //如果内存池不是空的,那么首先从里面获取空间
  35. if (pool != NULL)
  36. {
  37.   person = pool;
  38.   pool = pool->next;
  39.   count--;
  40. }
  41. //内存池为空,则直接申请
  42. else
  43. {
  44.   person = (struct Person*)malloc(sizeof(struct Person));
  45.   if (person == NULL)
  46.   {
  47.    printf("内存分配失败!\n");
  48.    exit(1);
  49.   }
  50. }


  51. getInput(person);

  52. //将person添加到通讯录中
  53. if (*contacts != NULL)
  54. {
  55.   temp = *contacts;
  56.   *contacts = person;
  57.   person->next = temp;
  58. }
  59. else
  60. {
  61.   *contacts = person;
  62.   person->next = NULL;
  63. }
  64. }

  65. void printPerson(struct Person* person)
  66. {
  67. printf("联系人:%s\n", person->name);
  68. printf("电话:%s\n", person->phone);
  69. }

  70. struct Person* findPerson(struct Person* contacts)
  71. {
  72. struct Person* current;
  73. char input[40];

  74. printf("请输入联系人:");
  75. scanf("%s", input);

  76. current = contacts;
  77. while (current != NULL && strcmp(current->name, input))
  78. {
  79.   current = current->next;
  80. }

  81. return current;
  82. }

  83. void changePerson(struct Person* contacts)
  84. {
  85. struct Person* person;

  86. person = findPerson(contacts);
  87. if (person == NULL)
  88. {
  89.   printf("找不到联系人!\n");
  90. }
  91. else
  92. {
  93.   printf("请输入联系电话:");
  94.   scanf("%s", person->phone);
  95. }
  96. }

  97. void delPerson(struct Person** contacts)
  98. {
  99. struct Person* person;
  100. struct Person* current;
  101. struct Person* previous;
  102. struct Person* temp;
  103. {

  104. };

  105. //先找到待删除的节点的指针
  106. person = findPerson(*contacts);
  107. if (person == NULL)
  108. {
  109.   printf("找不到该联系人!\n");
  110. }
  111. else
  112. {
  113.   current = *contacts;
  114.   previous = NULL;

  115.   //将current定位到待删除的节点
  116.   while (current != NULL && current != person)
  117.   {
  118.    previous = current;
  119.    current = current->next;
  120.   }

  121.   if (previous == NULL)
  122.   {
  123.    //若待删除的是第一个节点
  124.    *contacts = current->next;
  125.   }
  126.   else
  127.   {
  128.    //若待删除的不是第一个节点
  129.    previous->next = current->next;
  130.   }

  131.   //判断内存池中有没有空位
  132.   if (count < MAX)
  133.   {
  134.    //使用头插法将person指向的空间插入内存池中
  135.    if (pool != NULL)
  136.    {
  137.     temp = pool;
  138.     pool = person;
  139.     person->next = temp;
  140.    }
  141.    else
  142.    {
  143.     pool = person;
  144.     person->next = NULL;
  145.    }
  146.    count++;
  147.   }
  148.   //没有空位,直接释放
  149.   else
  150.   {
  151.    free(person);//将内存空间释放
  152.   }
  153. }
  154. }

  155. void displayContacts(struct Person* contacts)
  156. {
  157. struct Person* current;

  158. current = contacts;
  159. while (current != NULL)
  160. {
  161.   printPerson(current);
  162.   current = current->next;
  163. }
  164. }

  165. void releaseContacts(struct Person** contacts)
  166. {
  167. struct Person* temp;

  168. while (*contacts != NULL)
  169. {
  170.   temp = *contacts;
  171.   *contacts = (*contacts)->next;
  172.   free(temp);
  173. }
  174. }

  175. void releasePool(void)
  176. {
  177. struct Person* temp;
  178. while (pool != NULL)
  179. {
  180.   temp = pool;
  181.   pool = pool->next;
  182.   free(temp);
  183. }
  184. }

  185. int main(void)
  186. {
  187. int code;
  188. struct Person* contacts = NULL;
  189. struct Person* person;

  190. printf("| 欢迎使用通讯录管理程序 |\n");
  191. printf("|--- 1:插入新的联系人 ---|\n");
  192. printf("|--- 2:查找现有联系人 ---|\n");
  193. printf("|--- 3:更改现有联系人 ---|\n");
  194. printf("|--- 4:删除现有联系人 ---|\n");
  195. printf("|--- 5:显示当前通讯录 ---|\n");
  196. printf("|--- 6:退出通讯录程序 ---|\n");

  197. while (1)
  198. {
  199.   printf("\n请输入指令代码:");
  200.   scanf("%d", &code);
  201.   switch (code)
  202.   {
  203.   case 1:addPerson(&contacts); break;
  204.   case 2:person = findPerson(contacts);
  205.    if (person == NULL)
  206.    {
  207.     printf("找不到该联系人!\n");
  208.    }
  209.    else
  210.    {
  211.     printPerson(person);
  212.    }
  213.    break;
  214.   case 3:changePerson(contacts); break;
  215.   case 4:delPerson(&contacts); break;
  216.   case 5:displayContacts(contacts); break;
  217.   case 6:goto END;
  218.   }
  219. }

  220. END://此处直接跳出恒循环
  221. releaseContacts(&contacts);
  222. releasePool();

  223. return 0;

  224. }



 楼主| 盗铃何须掩耳 发表于 2022-5-6 17:02 | 显示全部楼层


typedef给数据类型起别名

C语言是一门古老的语言,它是在1969至1973年间,由两位天才丹尼斯·里奇和肯·汤普逊在贝尔实验室以B语言为基础开发出来的,用于他们的重写UNIX计划(这也为后来UNIX系统的可移植性打下了基础,之前的UNIX是使用汇编语言编写的,当然也是这两位为了玩一个自己设计的游戏而编写的)。天才就是和咱常人不一样,不过他俩的故事,在这篇里面不多啰嗦,我们回到话题。

虽然C语言诞生的很早,但是却依旧不是最早的高级编程语言。目前公认的最早的高级编程语言,是IBM公司于1957年开发的FORTRAN语言。C语言诞生之时,FORTRAN已经统领行业数十年之久。因此,C语言要想快速吸纳FORTRAN中的潜在用户,就必须做出一些妥协。

我们知道,不同的语言的语法,一般来说是不同的,甚至还有较大的差距。比如:

C:

  1. int a, b, c;
  2. float i, j, k;

而FORTRAN语言是这样的:

  1. integer :: a, b, c;
  2. real :: i, j, k;

如果让FORTRAN用户使用原来的变量名称进行使用,那么就能够快速迁移到C语言上面来,这就是typedef的用处之一。

我们使用FORTRAN语言的类型名,那就这么办:

  1. typedef int integer;
  2. typedef float real;

  3. integer a, b, c;
  4. real i, j, k;


您需要登录后才可以回帖 登录 | 注册

本版积分规则

50

主题

385

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部