[技术手册] C语言的内存池使用

[复制链接]
1206|7
 楼主| jf101 发表于 2024-4-7 16:25 | 显示全部楼层 |阅读模式


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. }
中国龙芯CDX 发表于 2024-4-10 10:51 | 显示全部楼层
楼主这个管理系统项目很不错,深入了解一下
szt1993 发表于 2024-4-10 11:25 | 显示全部楼层
内存池,实际上就是我们让程序创建出来的一块额外的缓存区域,如果有需要释放内存,先不必使用free函数,如果内存池有空,那么直接放入内存池。
小夏天的大西瓜 发表于 2024-4-11 17:25 | 显示全部楼层
确实申请和释放内存对于整体的程序设计非常重要
 楼主| jf101 发表于 2024-4-14 14:25 | 显示全部楼层
内存池使用单链表来进行维护,非常实用
小小蚂蚁举千斤 发表于 2024-4-15 10:04 | 显示全部楼层
自由地管理内存就必须去堆中申请,然后还需要考虑何时释放
OKAKAKO 发表于 2024-4-19 18:41 | 显示全部楼层
内存池,实际上就是我们让程序创建出来的一块额外的缓存区域,如果有需要释放内存,先不必使用free函数,如果内存池有空,那么直接放入内存池。
星辰大海不退缩 发表于 2024-4-21 12:29 | 显示全部楼层
这个管理系统项目适合新学习C语言的新手
您需要登录后才可以回帖 登录 | 注册

本版积分规则

262

主题

1929

帖子

3

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