首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

C - 通讯录2.0(详细解析)

  • 25-04-24 07:41
  • 3398
  • 12721
blog.csdn.net

目录

一.引入枚举

二.动态内存开辟

contact.h

1.初始化

最终代码

2.扩容

最终代码

3.退出自动销毁空间

4.减容

最终代码

三.依姓名排序


通讯录1.0里的 PeoInfo data[MAX] 一次开辟定了内存空间。我放150个人空间不够。只放15个人又太浪费空间。

我们对通讯录1.0进行改造升级。
扩容:方便测试,先给3个内存空间,以后当前联系人数量 == 最大联系人数量时,最大容量增加2个内存空间。
减容:当最大人数比现有人数多5个以上,减容最大人数3个内存空间。



一.引入枚举

test.c 的 switch 语句中的 case 1:   case 2:  ......我们写代码时很容易忘记这些 1,2......分别代表什么含义。
枚举常量的下标默认从0开始,每次增加1 。我们引入枚举常量。
这样在 case x:这里就能看到想要干什么。

test.c

  1. enum Option
  2. {
  3. EXIT,//0
  4. ADD, //1
  5. DEL, //2
  6. SEARCH,
  7. MODIFY,
  8. SHOW,
  9. SORT,
  10. DELALL
  11. };
  12. switch (input)
  13. {
  14. case ADD:
  15. AddContact(&con);
  16. break;
  17. case DEL:
  18. DelContact(&con);
  19. break;
  20. case SEARCH:
  21. SearchContact(&con);
  22. break;
  23. case MODIFY:
  24. ModifyContact(&con);
  25. break;
  26. case SHOW:
  27. ShowContact(&con);
  28. break;
  29. case SORT:
  30. break;
  31. case DELALL:
  32. DelAllContact(&con);
  33. break;
  34. case EXIT:
  35. printf("退出通讯录\n");
  36. break;
  37. default:
  38. printf("输入错误\n");
  39. break;
  40. }

二.动态内存开辟

我们要改造,PeoInfo data[MAX] 这里就不能是数组了。

contact.h

  1. //静态版本
  2. typedef struct Contact
  3. {
  4. PeoInfo data[MAX];//存放人的信息
  5. int sz;//当前已经存入联系人的个数
  6. }Contact;

我们要用 malloc 开辟一块空间,把地址存起来。
所以,要把 data 改为 PeoInfo * 类型的指针。由 data 指向开辟的空间。data 指向空间里存放人的信息。

除了 data,sz ,还需要记录当前最大容量。

  1. #include
  2. #define DEFAULT_SZ 3 //通讯录默认大小
  3. #define INC_SZ 2 //每次增加
  4. #define RED_SZ 3 //每次减少
  5. #define DIFF_SZ_CAPA 5 //最大 - 当前 的差值
  6. //动态版本
  7. typedef struct Contact
  8. {
  9. PeoInfo* data;//指向存放人信息的空间
  10. int sz;//当前已经存入联系人的个数
  11. int capacity;//当前通讯录的最大容量
  12. }Contact;

 现在data 还没有指向的空间。

1.初始化

contant.c

  1. //静态版本
  2. void InitContact(Contact* pc)
  3. {
  4. assert(pc);
  5. pc->sz = 0;
  6. memset(pc->data, 0, sizeof(pc->data));
  7. }

pc->sz = 0;肯定是不用变的。这里就不能用 memset 了,要用 malloc 开辟内存空间。


我们先开辟空间,再指定通讯录默认大小为3 。这样更合理。

  1. //动态版本
  2. void InitContact(Contact* pc)
  3. {
  4. assert(pc);
  5. pc->sz = 0;
  6. pc->data = (PeoInfo*)malloc(sizeof(PeoInfo) * DEFAULT_SZ);
  7. pc->capacity = DEFAULT_SZ;
  8. }

malloc 的参数是要开辟内存的大小(字节)。
所以我们代码中 malloc 参数部分是:一个人的大小 * 通讯录默认人数 = 要开辟字节数

malloc 返回类型 void*  。用 pc 指向的 data 指针接收。data 的类型:PeoInfo *   。所以强制转换。


malloc 是不对开辟的内存空间初始化的。上面这样写,我们要初始化。
所以我用 calloc 。它初始化内存空间的每个字节为0

  为 num 个大小为 size 的元素开辟内存空间。

  1. //动态版本
  2. void InitContact(Contact* pc)
  3. {
  4. assert(pc);
  5. pc->sz = 0;
  6. pc->data = (PeoInfo*)calloc(DEFAULT_SZ, sizeof(PeoInfo));
  7. pc->capacity = DEFAULT_SZ;
  8. }

同时,我们要对开辟的空间进行维护,判断是否开辟成功。
直接用 pc->data 接收不合适

最终代码

  1. //动态版本
  2. void InitContact(Contact* pc)
  3. {
  4. assert(pc);
  5. pc->sz = 0;
  6. PeoInfo* ptr = (PeoInfo*)calloc(DEFAULT_SZ, sizeof(PeoInfo));
  7. if (ptr == NULL)
  8. {
  9. perror("InitContact:calloc");
  10. return;
  11. }
  12. pc->data = ptr;
  13. pc->capacity = DEFAULT_SZ;
  14. }

2.扩容

contact.c

改为动态开辟版本,里面放了元素,放不下时,要考虑扩容问题。对 Add 更改。

  1. //静态版本
  2. void AddContact(Contact* pc)
  3. {
  4. assert(pc);
  5. if (pc->sz == MAX)
  6. {
  7. printf("通讯录已满,无法添加\n");
  8. return;
  9. }
  10. //增加一个人的信息
  11. printf("请输入名字:");......
  12. pc->sz++;
  13. }

动态版本不存在放满的情况,这里不用判断 == MAX


每次添加联系人后进行判断,当最大空间人数 capacity == 当前空间人数 sz 时,扩容。

  1. //动态版本
  2. void AddContact(Contact* pc)
  3. {
  4. assert(pc);
  5. //增加一个人的信息......
  6. if (pc->sz == pc->capacity)
  7. {
  8. //扩容
  9. }
  10. pc->sz++;
  11. }

用 realloc 对 data 指向的空间调整。扩容的地方分装成函数。

 
ptr 是要调整的内存地址   size 是调整后的新大小(字节)。返回开辟好新空间的起始地址。

若当最大空间人数 capacity != 当前空间人数 sz 时,不扩容,直接返回。
新大小(字节) = 增加后的人数 * 每个人的大小。
判断是否成功开辟新空间,若是,再将 ptr 传给 pc->data 

最终代码

  1. void check_capacity(Contact* pc)
  2. {
  3. assert(pc);
  4. if (pc->sz == pc->capacity)
  5. {
  6. //扩容
  7. PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
  8. if (ptr == NULL)
  9. {
  10. perror("check_capacity:realloc");
  11. return;
  12. }
  13. pc->data = ptr;
  14. pc->capacity += INC_SZ;//当前通讯录的最大容量再自增2
  15. }
  16. }
  17. //动态版本
  18. void AddContact(Contact* pc)
  19. {
  20. assert(pc);
  21. //增加一个人的信息
  22. printf("请输入名字:");
  23. scanf("%s", pc->data[pc->sz].name);
  24. printf("请输入年龄:");
  25. scanf("%d", &(pc->data[pc->sz].age));
  26. printf("请输入性别:");
  27. scanf("%s", pc->data[pc->sz].sex);
  28. printf("请输入地址:");
  29. scanf("%s", pc->data[pc->sz].addr);
  30. printf("请输入电话:");
  31. scanf("%s", pc->data[pc->sz].tele);
  32. pc->sz++;
  33. check_capacity(pc);
  34. printf("\n当前联系人数量:%d\n", pc->sz);
  35. printf("当前最大容量:%d\n", pc->capacity);
  36. }

3.退出自动销毁空间

当程序退出时,空间要销毁,free

  1. //test.c
  2. case EXIT:
  3. DestroyContact(&con);
  4. printf("退出通讯录\n");
  5. break;
  6. //contact.h 销毁内存空间
  7. void DestroyContact(Contact* pc);
  8. //contact.c
  9. void DestroyContact(Contact* pc)
  10. {
  11. assert(pc);
  12. free(pc->data);
  13. pc->data = NULL;
  14. pc->capacity = 0;
  15. pc->sz = 0;
  16. pc = NULL;
  17. }

4.减容

我们还是要判断通讯录是否为空,为空无法删除。
只需在 chack_capacity 函数中加上减容部分,再在 DelContact 函数中加几条语句即可。

最终代码

  1. void check_capacity(Contact* pc)
  2. {
  3. assert(pc);
  4. if (pc->sz == pc->capacity)
  5. {
  6. //扩容
  7. }
  8. if ((pc->capacity - pc->sz) > DIFF_SZ_CAPA)
  9. {
  10. //减容
  11. PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity - RED_SZ) * sizeof(PeoInfo));
  12. if (ptr == NULL)
  13. {
  14. perror("check_capacity:realloc");
  15. return;
  16. }
  17. pc->data = ptr;
  18. pc->capacity -= RED_SZ;
  19. }
  20. }
  21. //动态版本
  22. void DelContact(Contact* pc)
  23. {
  24. assert(pc);
  25. char name[NAME_MAX] = { 0 };
  26. if (pc->sz == 0)
  27. {
  28. printf("通讯录为空,无法删除\n");
  29. return;
  30. }
  31. //查找
  32. printf("请输入要删除的人的名字:>");
  33. scanf("%s", name);
  34. int ret = FindByName(pc, name);
  35. if (-1 == ret)
  36. {
  37. printf("要删除的人不存在\n");
  38. return;
  39. }
  40. int i = 0;
  41. //删除
  42. for (i = ret; i < pc->sz - 1; i++)
  43. {
  44. pc->data[i] = pc->data[i + 1];
  45. }
  46. pc->sz--;
  47. printf("删除成功\n");
  48. check_capacity(pc);
  49. printf("\n当前联系人数量:%d\n", pc->sz);
  50. printf("当前最大容量:%d\n", pc->capacity);
  51. }

三.依姓名排序

用 qsort 函数。(点击 qsort 查看详细说明)

  1. //contact.c
  2. int sortcon_byname(const void* e1, const void* e2)
  3. {
  4. return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
  5. }
  6. void SortContact_ByName(Contact* pc)
  7. {
  8. assert(pc);
  9. qsort(pc->data, pc->sz, sizeof(PeoInfo), sortcon_byname);
  10. printf("排序成功\n");
  11. }
  12. //test.c
  13. case SORT:
  14. SortContact_ByName(&con);
  15. break;
  16. //contact.h
  17. void SortContact_ByName(Contact* pc);

错误写法:

  1. int sortcon_byname(const void* e1, const void* e2)
  2. {
  3. return strcmp(((Contact*)e1)->data->name, ((Contact*)e2)->data->name);
  4. }

e1,e2 分别是要比较的两个元素的地址
比较的是人的信息PeoInfo 

我们发现,2.0版本的通讯录,程序关闭,不保存这次运行时Add的联系人。
下次运行,按5显示联系人时,是空白。
怎么解决?

期待通讯录3.0

注:本文转载自blog.csdn.net的祁同伟.的文章"https://blog.csdn.net/HDSTQTW/article/details/146609703"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

106
编程语言
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top