• 运用动态内存实现通讯录(增删查改+排序)


    目录

    前言:

    实现通讯录:

    1.创建和调用菜单:

     2.创建联系人信息和通讯录:

    3.初始化通讯录: 

    4.增加联系人:

    5.显示联系人: 

    6.删除联系人:

     ​编辑

    7.查找联系人:

    ​编辑

    8.修改联系人:

    ​编辑

    9.排序联系人: 

    ​编辑

    10.释放通讯录

    总结:


    前言:

    通讯录通常是一个记录联系人信息的电子或纸质文件,包括名称、电话号码、电子邮件地址、物理地址等。通讯录旨在方便人们在需要联系某个人或组织时快速找到相关信息。现代通讯录通常是数字化的,可以存储在计算机、智能手机或云服务器中,也可以在社交媒体等在线平台上创建。通讯录是现代社交和商务通信的重要工具之一,有助于帮助人们管理他们的联系人,从而更好地进行社交和商务交流。

    以下是一个简单的C语言实现通讯录的例子:

    1. #include
    2. #include
    3. #define MAX_CONTACTS 100 // 最大联系人数
    4. struct Contact {
    5. char name[50];
    6. char phone_num[20];
    7. char email[50];
    8. }; // 联系人结构体
    9. int main() {
    10. struct Contact contacts[MAX_CONTACTS]; // 联系人数组
    11. int num_contacts = 0; // 当前联系人数
    12. while (1) {
    13. printf("请选择操作:\n");
    14. printf("1. 添加联系人\n");
    15. printf("2. 显示所有联系人\n");
    16. printf("3. 退出\n");
    17. int action;
    18. scanf("%d", &action);
    19. if (action == 1) { // 添加联系人
    20. if (num_contacts == MAX_CONTACTS) {
    21. printf("联系人数量已达上限\n");
    22. } else {
    23. struct Contact new_contact;
    24. printf("请输入联系人姓名:\n");
    25. scanf("%s", new_contact.name);
    26. printf("请输入联系人电话号码:\n");
    27. scanf("%s", new_contact.phone_num);
    28. printf("请输入联系人电子邮件地址:\n");
    29. scanf("%s", new_contact.email);
    30. contacts[num_contacts] = new_contact;
    31. num_contacts++;
    32. printf("联系人添加成功\n");
    33. }
    34. } else if (action == 2) { // 显示所有联系人
    35. printf("当前联系人如下:\n");
    36. printf("姓名\t电话\t邮箱\n");
    37. for (int i = 0; i < num_contacts; i++) {
    38. printf("%s\t%s\t%s\n", contacts[i].name, contacts[i].phone_num, contacts[i].email);
    39. }
    40. } else if (action == 3) { // 退出
    41. printf("程序已退出\n");
    42. break;
    43. } else {
    44. printf("输入无效,请重新输入\n");
    45. }
    46. }
    47. return 0;
    48. }

    这个程序使用一个结构体数组来存储所有联系人的信息。通过一个循环菜单来实现添加联系人、显示所有联系人和退出等操作。程序可以根据需要进行修改和扩展。

    以上是最基础最基本的通讯录实现,

    而本文则会运用动态内存对通讯录实现改进操作。

    实现通讯录:

    1.创建和调用菜单:

    我们在之前的blog中都对菜单进行了创建,虽然说之前都是关于实现游戏的菜单,但这次也不例外。

    通讯录也应当拥有一个菜单。

    具体的代码我不多做赘述,如下:
     

    1. void menu()
    2. {
    3. printf("**************Contact**************\n");
    4. printf("***********************************\n");
    5. printf("*******1.ADD 2.DEL*********\n");
    6. printf("*******3.SEARCH 4.MODIFY******\n");
    7. printf("*******5.SHOW 6.SORT********\n");
    8. printf("*************0.EXIT****************\n");
    9. printf("***********************************\n");
    10. }

    接下来就是我们的do...while循环和switch语句的实现了,以上菜单代码在我的之前三篇blog中都有讲解,需要可以跳转到:

    C语言实现《扫雷》_无双@的博客-CSDN博客 

    C语言实现《三子棋》游戏-CSDN博客

    C语言实现《猜数字游戏》_无双@的博客-CSDN博客

    如今我们学习了结构体的相关知识,并认识了有关枚举的结构体类型,我们不妨尝试一下:

    1. enum option
    2. {
    3. EXIT,
    4. ADD,
    5. DEL,
    6. SEARCH,
    7. MODIFY,
    8. SHOW,
    9. SORT
    10. };

    则我们在main函数里就可以写成:

    1. int main()
    2. {
    3. int input = 0;
    4. do
    5. {
    6. menu();
    7. printf("请选择:>");
    8. scanf("%d", &input);
    9. switch (input)
    10. {
    11. case ADD:
    12. break;
    13. case DEL:
    14. break;
    15. case SEARCH:
    16. break;
    17. case MODIFY:
    18. break;
    19. case SHOW:
    20. break;
    21. case SORT:
    22. break;
    23. case EXIT:
    24. printf("正在退出...\n");
    25. break;
    26. default:
    27. printf("输入错误...\n");
    28. }
    29. } while (input);
    30. return 0;
    31. }

    运用枚举的好处就是,我们只要想调用ADD就直接输入1,不管ADD是case1里还是case2里。

    如此则可以方便我们的操作,在以后的代码的实现,我们应当尽量尝试去使用枚举来实现菜单。

     2.创建联系人信息和通讯录:

    我们在创建联系人信息和通讯录的时候,是面向联系人和通讯录这两个对象的,因此我们不妨使用结构体来创建。

    代码如下:

    1. #define NAME_MAX 20
    2. #define SEX_MAX 5
    3. #define TELE_MAX 13
    4. #define ADDR_MAX 40
    5. typedef struct PeoInfo
    6. {
    7. char name[NAME_MAX];
    8. int age;
    9. char sex[SEX_MAX];
    10. char tele[TELE_MAX];
    11. char addr[ADDR_MAX];
    12. }PeoInfo;
    13. typedef struct Contact
    14. {
    15. PeoInfo* data;
    16. int sz;//记录当前联系人的个数
    17. int capacity;//记录当前通讯录的容量
    18. }Contact;

    联系人里应当有“名字”,“年龄”,“性别”,“电话”,“地址”。

    通讯录里应当有“指向联系人的指针”,“记录当前联系人的个数”,“记录当前通讯录的容量”。

    指针和容量是为之后动态开辟内存做好准备。

    它们之间的关系如图:

    3.初始化通讯录: 

     在完成上述的操作后,我们则可以开始对我们的通讯录进行操作。

    第一步肯定得是初始化通讯录,

    在这一步则是我们运用动态内存开辟空间的最佳时机。

    代码如下:

    1. #define DEFAULT_SZ 3
    2. int main()
    3. {
    4. Contact con;
    5. InitContact(&con);
    6. }
    7. void InitContact(Contact* pc)
    8. {
    9. assert(pc);
    10. pc->sz = 0;
    11. pc->capacity = DEFAULT_SZ;
    12. pc->data = (PeoInfo*)calloc(DEFAULT_SZ, sizeof(PeoInfo));
    13. if (pc->data == NULL)
    14. {
    15. perror("calloc->InitContact");
    16. return;
    17. }
    18. }

    由于我们之前已经讲解过结构体传参,对于结构体传参的最佳办法是传递地址,所以我们运用指针来进行操作。

    我们使用calloc开辟空间,是因为使用calloc就可以帮我们对开辟好的空间直接初始化为0,这样可大大节省代码量,并且使得代码更为整洁。

    pc->capacity = 3的意思是让空间容量在初始化的时候最多可以放下三个联系人,如果不够了我们就继续增加,使用动态内存开辟空间的优势就在这里:
    方便我们进行追加联系人。  

    4.增加联系人:

    在我们增加联系人的时候,我们首先需要判断我们开辟好的空间是否够用,这个时候我们应当在contact.c的文件中创建一个函数,用来检查空间是否够用。

    函数代码如下:

    1. #define DEFAULT_INC 2
    2. static void Check_Capacity(Contact* pc)
    3. {
    4. assert(pc);
    5. if (pc->sz == pc->capacity)
    6. {
    7. PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (DEFAULT_INC + DEFAULT_SZ)*sizeof(PeoInfo));
    8. if (ptr != NULL)
    9. {
    10. pc->data = ptr;
    11. pc->capacity += DEFAULT_INC;
    12. printf("增容成功!\n");
    13. }
    14. else
    15. {
    16. perror("realloc->AddContact");
    17. return;
    18. }
    19. }
    20. }

    这里要注意的是我们在使用realloc追加空间的时候,应当创建一个临时指针,先用于判断realloc是否可以开辟成功,如果可以则赋值到pc->data处,这样可以使得代码风格更加健壮。

    但我们判断完后,就要对代码进行添加操作,代码如下:

    1. void AddContact(Contact* pc)
    2. {
    3. assert(pc);
    4. Check_Capacity(pc);
    5. printf("请输入名字:>");
    6. scanf("%s", pc->data[pc->sz].name);
    7. printf("请输入年龄:>");
    8. scanf("%d", &(pc->data[pc->sz].age));
    9. printf("请输入性别:>");
    10. scanf("%s", pc->data[pc->sz].sex);
    11. printf("请输入电话:>");
    12. scanf("%s", pc->data[pc->sz].tele);
    13. printf("请输入地址:>");
    14. scanf("%s", pc->data[pc->sz].addr);
    15. pc->sz++;
    16. printf("添加成功!\n");
    17. }

    5.显示联系人: 

    我们在实现增加联系人后,可以将已经存在的联系人信息打印出来,方便我们查看。

    具体的代码如下:

    1. void ShowContact(Contact* pc)
    2. {
    3. assert(pc);
    4. if (pc->sz == 0)
    5. {
    6. printf("通讯录为空!\n");
    7. return;
    8. }
    9. printf("%-20s%-5s%-5s%-12s%-30s\n", "名字", "年龄", "性别", "电话", "地址");
    10. for (int i = 0; i < pc->sz; i++)
    11. {
    12. printf("%-20s%-5d%-5s%-12s%-30s\n",
    13. pc->data[i].name,
    14. pc->data[i].age,
    15. pc->data[i].sex,
    16. pc->data[i].tele,
    17. pc->data[i].addr);
    18. }
    19. }

    我们想要打印出来的格式较为整齐,

    所以采取使用 

    printf("%-20s%-5s%-5s%-12s%-30s\n", "名字", "年龄", "性别", "电话", "地址");

     这样我们的输出结果就为这样:

    6.删除联系人:

    对于删除练习人,我们要进行的第一步操作当然需要找到联系人。

    所以我们可以尝试创建一个函数用来查找联系人:

    代码如图所示:

    1. static int FindByName(Contact* pc, char* name)
    2. {
    3. assert(pc && name);
    4. for (int i = 0; i < pc->sz; i++)
    5. {
    6. if (strcmp(pc->data[i].name,name) == 0)
    7. {
    8. return i;
    9. }
    10. }
    11. //找不到
    12. return -1;
    13. }

    name数组是在DelContact函数里创建的,用来输入名字。

    这里我们运用了字符串比较函数,strcmp,如果它们相等则会等于0,,就说明找到了该联系人,则返回通讯录里的第i个联系人。

    则我们在DelContact就可以进行删除操作。

    具体的方法就是将后面的联系人一个一个与前一个进行替换。

    代码如下:

    1. for (int i = ret; i < pc->sz - 1; i++)
    2. {
    3. pc->data[i] = pc->data[i + 1];
    4. }
    5. pc->sz--;
    6. printf("该联系人已删除\n");

    如此一来删除联系人代码完成,

    完整代码如下:

    1. void DelContact(Contact* pc)
    2. {
    3. assert(pc);
    4. char name[NAME_MAX];
    5. if (pc->sz == 0)
    6. {
    7. printf("通讯录为空\n");
    8. return;
    9. }
    10. printf("请输入你要删除联系人的名字:>");
    11. scanf("%s", name);
    12. int ret = FindByName(pc, name);
    13. if (ret == -1)
    14. {
    15. printf("该联系人不存在\n");
    16. return;
    17. }
    18. for (int i = ret; i < pc->sz - 1; i++)
    19. {
    20. pc->data[i] = pc->data[i + 1];
    21. }
    22. pc->sz--;
    23. printf("该联系人已删除\n");
    24. }

     

    7.查找联系人:

    查找联系人与上述相似,先查找名字,再进行输出,输出则用到的是ShowContact里的代码:
    在这里我不做过的赘述,完整代码如下:

    1. void SearchContact(Contact* pc)
    2. {
    3. assert(pc);
    4. char name[NAME_MAX];
    5. if (pc->sz == 0)
    6. {
    7. printf("通讯录为空!\n");
    8. return;
    9. }
    10. printf("请输入你要查找联系人的名字:>");
    11. scanf("%s", name);
    12. int ret = FindByName(pc, name);
    13. if (ret == -1)
    14. {
    15. printf("不存在该联系人\n");
    16. return;
    17. }
    18. printf("%-20s%-5s%-5s%-12s%-30s\n", "名字", "年龄", "性别", "电话", "地址");
    19. printf("%-20s%-5d%-5s%-12s%-30s\n",
    20. pc->data[ret].name,
    21. pc->data[ret].age,
    22. pc->data[ret].sex,
    23. pc->data[ret].tele,
    24. pc->data[ret].addr);
    25. }

    8.修改联系人:

    修改联系人也和上述相似,先查找再进行修改,这次的修改则是运用了AddContact函数里的部分代码,

    完整代码如下:

    1. void ModifyContact(Contact* pc)
    2. {
    3. assert(pc);
    4. char name[NAME_MAX];
    5. if (pc->sz == 0)
    6. {
    7. printf("通讯录为空\n");
    8. return;
    9. }
    10. printf("请输入你要修改联系人的名字:>");
    11. scanf("%s", name);
    12. int ret = FindByName(pc, name);
    13. if (ret == -1)
    14. {
    15. printf("不存在该联系人\n");
    16. return;
    17. }
    18. printf("请输入名字:>");
    19. scanf("%s", pc->data[ret].name);
    20. printf("请输入年龄:>");
    21. scanf("%d", &(pc->data[ret].age));
    22. printf("请输入性别:>");
    23. scanf("%s", pc->data[ret].sex);
    24. printf("请输入电话:>");
    25. scanf("%s", pc->data[ret].tele);
    26. printf("请输入地址:>");
    27. scanf("%s", pc->data[ret].addr);
    28. printf("修改成功!\n");
    29. }

    9.排序联系人: 

    对联系人的排序我们可以运用qsort函数来进行排序,如果忘记了该函数可以参考之前blog中对qsort函数的讲解:

    自主实现qsort函数-CSDN博客

    接下来我们则可以实现:

    1.按照名字大小来排序。

    2.按照年龄大小来排序。

    代码如下:

    1. static int cmp(const void* e1, const void* e2)
    2. {
    3. //return (((PeoInfo*)e1)->age > ((PeoInfo*)e2)->age) ? 1 : -1;//年龄排序
    4. return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);//姓名排序
    5. }
    6. void SortContact(Contact* pc)
    7. {
    8. assert(pc);
    9. if (pc->sz == 0)
    10. {
    11. printf("通讯录为空!\n");
    12. return;
    13. }
    14. qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp);
    15. printf("排序成功!\n");
    16. }

    10.释放通讯录

    如今我们想要离开通讯录时,因为使用了动态内存的方式开辟内存,就不得不对其进行释放。

    对于释放的操作,我们则可以创建一个DestoryContact函数来进行释放,以及销毁。

    具体方式如下:

    1. void DestoryContact(Contact* pc)
    2. {
    3. assert(pc);
    4. free(pc->data);
    5. pc->data = NULL;
    6. pc->sz = 0;
    7. pc->capacity = 0;
    8. }

     如此一来通讯录操作完成。

    总结:

    本文实现了动态内存开辟实现通讯录。

    该通讯录还存在一些问题,例如无法保存该数据,随着程序的结束,通讯录内容也就此结束。

    因此我们下一次可以尝试使用文件操作来编写通讯录。

    一下是我的Gitee仓库可以参考以上代码:

    test_c_with_X1: 本仓库里的代码为c语言的测试代码 - Gitee.com

    学习完后可以动手尝试编写编写。

    记住

    “坐而言不如起而行”

    Action speaker louder than words!

  • 相关阅读:
    Linux环境下conda虚拟环境的迁移
    如何使用 apt-get 安装特定版本的软件包
    继承、抽象类、代码块
    【译】使用最新预览版查看您的拉请求注释
    Nmap端口服务 之 CentOS7 关于启动Apache(httpd)服务、telnet服务、smtp服务、ftp服务、sftp服务
    Python中的函数和模块
    mmc子系统分析(二)
    otn 709帧结构
    图像滤波总结
    透视虎牙斗鱼三季报:游戏直播在各自“求变”中见分晓
  • 原文地址:https://blog.csdn.net/weixin_72917087/article/details/133467747