• 【C语言】——通讯录(静态-动态增长-文件储存)


     

    目录

    前言:

    一:整体框架

    关于通讯录结构体的创建 

    二:通讯录的功能实现(静态)

    2.1初始化通讯录

    2.2增加联系人

    2.3打印通讯录

    2.4删除联系人

     2.5 查找联系人

    2.6修改联系人 

    2.7排序联系人

    三:通讯录优化——动态内存

     3.1通讯录的创建

    3.2初始化通讯录 

    3.3增加联系人 

    3.4清空通讯录 

     四:通讯录优化——文件版本

    4.1退出保存信息到文件

     4.2初始化时加载文件信息

    五:整体代码

    test.c

     contact.c

    contact.h

    前言:

    在之前的篇章中讲述了【C语言】进阶——结构体【C语言】进阶——动态内存【C语言】进阶——文件操作

    在本篇运用以上知识结合来写一个小项目——通讯录

    我会逐步从静态版本优化到动态增加以及最终的文件存储版,循循渐进,详解通讯录的实现

    实现思路

    通讯录类似一个复杂结构体,包含了很多信息,以个人信息的通讯录而言,需要包含个人名字,年龄,电话,性别以及地址;

    而它所具有的基本功能:增(Add)删(Del) 改(modify) 查 (Search);以及我们可以对其一些简单的拓展功能;

    这些具体分析为:

    1. 打印一个菜单,提供用户选择功能;
    2. 添加联系人信息;
    3. 删除联系人信息;
    4. 查询联系人信息;
    5. 修改联系人信息;
    6. 显示所有联系人信息;
    7. 对所有联系人信息进行排序整理;
    8. 删除所有联系人信息;
    9. 操作完毕可选择退出。

    将整个项目分为三部分 :

    test.c ——测试代码模块

    contact.c——函数的实现

    contact.h——类型的定义,函数声明等 

    一:整体框架

    我们需要映入眼帘的菜单选择和提示,可以选择do...while();和switch语句相结合,来打印出整个框架体系;

    另外因为switch语句中case 1,case2...这样的数字不好看,我们可以采用枚举变量来定义;

            枚举的关键字是enum

    在括号内部注意每个成员名后面要加逗号,同时别忘了括号外面的分号

    枚举内部的成员被依次赋值为0,1,2

    我们假如将female赋值为1,secret就被赋值为2,male赋值为0

            枚举的优点:

    1.增加代码的可读性和可维护性

    2. 和 #define 定义的标识符比较枚举有类型检查,更加严谨。

    3. 防止了命名污染(封装)

    4. 便于调试

    5. 使用方便,一次可以定义多个常量

    1. void menu()
    2. {
    3. printf("<-==============通讯录=============->\n");
    4. printf("<-==========1.增加联系人===========->\n");
    5. printf("<-==========2.删除联系人===========->\n");
    6. printf("<-==========3.查找联系人===========->\n");
    7. printf("<-==========4.修改联系人===========->\n");
    8. printf("<-==========5.打印通讯录===========->\n");
    9. printf("<-==========6.排序通讯录===========->\n");
    10. printf("<-==========0.退出通讯录===========->\n");
    11. printf("<-=================================->\n");
    12. }
    13. enum Option
    14. {
    15. EXIT, //0
    16. ADD,
    17. DEL,
    18. SEARCH,
    19. MODIFY,
    20. SHOW,
    21. SORT
    22. };
    23. int main()
    24. {
    25. int input = 0;
    26. do
    27. {
    28. //菜单
    29. menu();
    30. printf("请输入操作:");
    31. scanf("%d", &input);
    32. switch (input)
    33. {
    34. case ADD:
    35. printf("增加联系人\n");
    36. break;
    37. case DEL:
    38. printf("删除联系人\n");
    39. break;
    40. case SEARCH:
    41. printf("查找联系人\n");
    42. break;
    43. case MODIFY:
    44. printf("修改联系人\n");
    45. break;
    46. case SHOW:
    47. printf("打印联系人\n");
    48. break;
    49. case SORT:
    50. printf("排序联系人\n");
    51. break;
    52. case EXIT:
    53. printf("退出通讯录\n");
    54. break;
    55. default:
    56. printf("输入错误,重新输入\n");
    57. break;
    58. }
    59. } while (input);
    60. return 0;
    61. }

     

    关于通讯录结构体的创建 

    创建所需的个人信息结构体

    利用#define 定义常量,有利于维护;

    1. #define NAME_MAX 20
    2. #define SEX_MAX 5
    3. #define TELE_MAX 12
    4. #define ADDR_MAX 30
    5. typedef struct Peo //个人信息结构体
    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. }Peo;

    另外还需要创建一个结构体,来记录Peo的信息,以数组来存放,创建变量,记录个数 

    1. typedef struct Contact
    2. {
    3. Peo data[DATA_MAX]; //存放个人信息
    4. int sz; //记录信息人数
    5. }Contact;

    二:通讯录的功能实现(静态)

    2.1初始化通讯录

    1. //静态-初始化通讯录
    2. void InitContact(Contact* pc)
    3. {
    4. assert(pc);
    5. pc->sz = 0;
    6. memset(pc->data, 0, sizeof(pc->data));
    7. }

    memset来初始化,以字节为单位一个字节一个字节的初始化! 

    2.2增加联系人

    增加之前,先判断是否为满,增加成功后,还需要将sz++,记录加入的人数

    1. //增加联系人
    2. void AddContact(Contact* pc)
    3. {
    4. assert(pc);
    5. //判断通讯录是否满了
    6. if (pc->sz == DATA_MAX)
    7. {
    8. printf("通讯录已满,增加失败\n");
    9. return;
    10. }
    11. //增加信息
    12. printf("请输入名字:");
    13. scanf("%s", pc->data[pc->sz].name);
    14. printf("请输入年龄:");
    15. scanf("%d", &(pc->data[pc->sz].age));
    16. printf("请输入性别:");
    17. scanf("%s", pc->data[pc->sz].sex);
    18. printf("请输入电话:");
    19. scanf("%s", pc->data[pc->sz].tele);
    20. printf("请输入地址:");
    21. scanf("%s", pc->data[pc->sz].addr);
    22. //对sz增加,
    23. pc->sz++;
    24. printf("增加成功\n");
    25. }

     

    2.3打印通讯录

    增加联系人后,真的在内存中增加与否,并不知道,我们需要打印出来

    先判断是否有信息,然后打印标题,增加可读性,再利用for循环,将sz个信息打印出来 

    -20就代表域宽是20(长度不够20用空格填充),负号代表左对齐,默认是右对齐的!

    1. //显示所有的联系人
    2. void ShowContact(const Contact* pc)
    3. {
    4. assert(pc);
    5. //判断通讯录内是否有信息
    6. if (pc->sz == 0)
    7. {
    8. printf("打印失败,通讯录为空\n");
    9. return;
    10. }
    11. //打印
    12. //打印标题
    13. printf("%-20s%-5s%-5s%-12s%-30s\n", "名字", "年龄", "性别", "电话", "地址");
    14. //打印信息
    15. int i = 0;
    16. for (i = 0; i < pc->sz; i++)
    17. {
    18. //打印每个人的信息
    19. printf("%-20s%-5d%-5s%-12s%-30s\n",
    20. pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
    21. }
    22. }

    2.4删除联系人

    1. 先判断通讯录是否有信息,通过名字来查找是否存在此人,
    2. 封装函数:因为我们在查找联系人,修改联系人中也会用到此功能。查找到此人,返回sz位置,没有此人,返回-1;
    3.  通过for循环:假如要删除人为 tmp的信息;就只需要把tmp后的数据往前移动,覆盖掉tmp位置的信息就可以了;
    4. 如果要删除最后一个人,则直接sz--;可以不进行访问,算是删除了
    5. 删除成功后,就把sz--;说明数组里的有效数据减1!
    1. //通过名字查找被删除人
    2. static int FindByName(Contact* pc, char* name)
    3. {
    4. assert(pc);
    5. int i = 0;
    6. for (i = 0; i < pc->sz; i++)
    7. {
    8. if (strcmp(pc->data[i].name, name) == 0)
    9. {
    10. return i;
    11. }
    12. }
    13. return -1;//找不到
    14. }
    15. //删除联系人
    16. void DelContact(Contact* pc)
    17. {
    18. assert(pc);
    19. //判断是否为空
    20. if (pc->sz == 0)
    21. {
    22. printf("删除失败,通讯录为空\n");
    23. return;
    24. }
    25. //查找是否存在此人
    26. char name[NAME_MAX];
    27. printf("请输入被删除人名字:\n");
    28. scanf("%s", name);
    29. //查找此人,存在返回位置,不存在返回-1
    30. int ret = FindByName(pc,name);
    31. if (ret == -1)
    32. {
    33. printf("要删除的人不存在\n");
    34. return;
    35. }
    36. //删除(覆盖)此人
    37. for (int i = ret; i < pc->sz - 1; i++) //sz-1 是防止执行体越界,如果要删除的是最后一个元素,通过sz--,直接不访问,
    38. {
    39. pc->data[i] = pc->data[i + 1];
    40. }
    41. pc->sz --;
    42. printf("删除成功\n");
    43. }

     

     

     2.5 查找联系人

    跟删除同理,先查找是否存在此人,查找到了返回下标打印出来即可 

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

    2.6修改联系人 

    查找到此人位置,记录返回,通过该位置进行修改 

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

     

    2.7排序联系人

    利用qsort快速排序,然后我们以名字strcmp函数来比较

    1. //目录排序
    2. //void qsort(void* base, size_t num, size_t size,
    3. // int (*compar)(const void*, const void*));
    4. int cmpare(const void* p1, const void* p2)
    5. {
    6. return strcmp(((Peo*)p1)->name, ((Peo*)p2)->name);
    7. }
    8. void SortContact(Contact* pc)
    9. {
    10. qsort(pc->data, pc->sz, sizeof(Peo), cmpare);
    11. }

    三:通讯录优化——动态内存

     3.1通讯录的创建

    增加变量capacity——用来记录通讯录容量

    利用指针动态创建内存,结合malloc,realloc,calloc

    1. typedef struct Contact
    2. {
    3. Peo* data; //存放数据
    4. int sz; //记录信息人数
    5. int capacity; //记录的是通讯录的当前容量
    6. }Contact;

    3.2初始化通讯录 

    利用calloc 开辟动态空间给data 

    1. #define DEFAULT_SZ 3 //默认容量
    2. #define DEFAULT_INC 2 //扩容量
    1. //动态版本的初始化
    2. void InitContact(Contact* pc)
    3. {
    4. assert(pc);
    5. pc->sz = 0;
    6. pc->capacity = DEFAULT_SZ;
    7. pc->data = calloc(pc->capacity, sizeof(Peo));
    8. if (pc->data == NULL)
    9. {
    10. perror("InitContact->calloc");
    11. return;
    12. }
    13. }

    3.3增加联系人 

     利用realloc来进行动态扩容,存放到临时变量ptr里,开辟成功给data

    1. //检查是否需要扩容
    2. void CheckCapacity(Contact* pc)
    3. {
    4. if (pc->sz == pc->capacity)
    5. {
    6. Peo* ptr = (Peo*)realloc(pc->data, (pc->capacity + DEFAULT_INC) * sizeof(Peo));
    7. if (ptr != NULL)
    8. {
    9. pc->data = ptr;
    10. pc->capacity += DEFAULT_INC;
    11. printf("增容成功\n");
    12. }
    13. else
    14. {
    15. perror("AddContact->realloc");
    16. return;
    17. }
    18. }
    19. }
    20. //增加联系人
    21. void AddContact(Contact* pc)
    22. {
    23. assert(pc);
    24. //增加容量
    25. CheckCapacity(pc);
    26. //增加信息
    27. printf("请输入名字:");
    28. scanf("%s", pc->data[pc->sz].name);
    29. printf("请输入年龄:");
    30. scanf("%d", &(pc->data[pc->sz].age));
    31. printf("请输入性别:");
    32. scanf("%s", pc->data[pc->sz].sex);
    33. printf("请输入电话:");
    34. scanf("%s", pc->data[pc->sz].tele);
    35. printf("请输入地址:");
    36. scanf("%s", pc->data[pc->sz].addr);
    37. pc->sz++;
    38. printf("增加成功\n");
    39. }

    3.4清空通讯录 

    因为涉及到了动态开辟,所以使用后要进行free空间

    防止内存泄漏

    写到EXIT退出通讯录里面,让它退出直接调用这个清空销毁函数!就算不销毁最终程序结束也会自动销毁!

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

     四:通讯录优化——文件版本

    4.1退出保存信息到文件

    使用文件操作,涉及到了文件缓冲区;

    ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序 中每一个正在使用的文件开辟一块“文件缓冲区”。

    ① 从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。

    ② 从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓 冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。

    ③ 缓冲区的大小根据C编译系统决定的。

    因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。 如果不做,可能导致读写文件的问题。

     

    先把信息存储起来后,在执行销毁并退出! 

    1. //保存信息到文件
    2. void SaveContact(Contact* pc)
    3. {
    4. FILE* pf = fopen("contact.txt", "wb"); //二进制读文件
    5. if (pf == NULL)
    6. {
    7. perror("SaveContact");
    8. return;
    9. }
    10. //写信息到文件
    11. int i = 0;
    12. for (i = 0; i < pc->sz; i++)
    13. {
    14. //fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pf);
    15. fwrite(pc->data + i, sizeof(Peo), 1, pf);
    16. }
    17. fclose(pf);
    18. pf = NULL;
    19. }

     4.2初始化时加载文件信息

    将信息加载到文件当中,

    在初始化阶段加载文件信息

    1. void CheckCapacity(Contact* pc);//检查上次保存的信息是否需要扩容
    2. void LoadContact(Contact* pc)
    3. {
    4. FILE* pf = fopen("contact.txt", "rb"); //以二进制读文件
    5. if (pf == NULL)
    6. {
    7. perror("LoadContact");
    8. return;
    9. }
    10. //读文件
    11. Peo tmp = { 0 }; //临时变量
    12. while (fread(&tmp, sizeof(Peo), 1, pf))
    13. {
    14. CheckCapacity(pc);
    15. pc->data[pc->sz] = tmp;
    16. pc->sz++;
    17. }
    18. fclose(pf);
    19. pf = NULL;
    20. }
    21. //文件版本的初始化函数
    22. void InitContact(Contact* pc)
    23. {
    24. assert(pc);
    25. pc->sz = 0;
    26. pc->capacity = DEFAULT_SZ;
    27. pc->data = calloc(pc->capacity, sizeof(Peo));
    28. if (pc->data == NULL)
    29. {
    30. perror("InitContact->calloc");
    31. return;
    32. }
    33. //加载文件中的信息到通讯录
    34. LoadContact(pc);
    35. }

     

    五:整体代码

    test.c

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. #include "contact.h"
    4. void menu()
    5. {
    6. printf("<-==============通讯录=============->\n");
    7. printf("<-==========1.增加联系人===========->\n");
    8. printf("<-==========2.删除联系人===========->\n");
    9. printf("<-==========3.查找联系人===========->\n");
    10. printf("<-==========4.修改联系人===========->\n");
    11. printf("<-==========5.打印通讯录===========->\n");
    12. printf("<-==========6.排序通讯录===========->\n");
    13. printf("<-==========0.退出通讯录===========->\n");
    14. printf("<-=================================->\n");
    15. }
    16. enum Option
    17. {
    18. EXIT, //0
    19. ADD,
    20. DEL,
    21. SEARCH,
    22. MODIFY,
    23. SHOW,
    24. SORT
    25. };
    26. int main()
    27. {
    28. int input = 0;
    29. Contact con; //创建通讯录
    30. //初始化通讯录
    31. InitContact(&con);
    32. do
    33. {
    34. //菜单
    35. menu();
    36. printf("请输入操作:");
    37. scanf("%d", &input);
    38. switch (input)
    39. {
    40. case ADD:
    41. printf("增加联系人\n");
    42. AddContact(&con);
    43. break;
    44. case DEL:
    45. printf("删除联系人\n");
    46. DelContact(&con);
    47. break;
    48. case SEARCH:
    49. printf("查找联系人\n");
    50. FindContact(&con);
    51. break;
    52. case MODIFY:
    53. printf("修改联系人\n");
    54. ModifyContact(&con);
    55. break;
    56. case SHOW:
    57. printf("打印联系人\n");
    58. ShowContact(&con);
    59. break;
    60. case SORT:
    61. printf("排序联系人\n");
    62. SortContact(&con);
    63. break;
    64. case EXIT:
    65. printf("退出通讯录\n");
    66. SaveContact(&con);
    67. DestroyContact(&con);
    68. break;
    69. default:
    70. printf("输入错误,重新输入\n");
    71. break;
    72. }
    73. } while (input);
    74. return 0;
    75. }

     contact.c

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include "contact.h"
    3. 静态-初始化通讯录
    4. //void InitContact(Contact* pc)
    5. //{
    6. // assert(pc);
    7. // pc->sz = 0;
    8. // memset(pc->data, 0, sizeof(pc->data));
    9. //}
    10. 动态版本的初始化
    11. //void InitContact(Contact* pc)
    12. //{
    13. // assert(pc);
    14. // pc->sz = 0;
    15. // pc->capacity = DEFAULT_SZ;
    16. // pc->data = calloc(pc->capacity, sizeof(Peo));
    17. // if (pc->data == NULL)
    18. // {
    19. // perror("InitContact->calloc");
    20. // return;
    21. // }
    22. //}
    23. void CheckCapacity(Contact* pc);//检查上次保存的信息是否需要扩容
    24. void LoadContact(Contact* pc)
    25. {
    26. FILE* pf = fopen("contact.txt", "rb");
    27. if (pf == NULL)
    28. {
    29. perror("LoadContact");
    30. return;
    31. }
    32. //读文件
    33. Peo tmp = { 0 };
    34. while (fread(&tmp, sizeof(Peo), 1, pf))
    35. {
    36. CheckCapacity(pc);
    37. pc->data[pc->sz] = tmp;
    38. pc->sz++;
    39. }
    40. fclose(pf);
    41. pf = NULL;
    42. }
    43. //文件版本的初始化函数
    44. void InitContact(Contact* pc)
    45. {
    46. assert(pc);
    47. pc->sz = 0;
    48. pc->capacity = DEFAULT_SZ;
    49. pc->data = calloc(pc->capacity, sizeof(Peo));
    50. if (pc->data == NULL)
    51. {
    52. perror("InitContact->calloc");
    53. return;
    54. }
    55. //加载文件中的信息到通讯录
    56. LoadContact(pc);
    57. }
    58. //
    59. 静态增加联系人
    60. //void AddContact(Contact* pc)
    61. //{
    62. // assert(pc);
    63. // //判断通讯录是否满了
    64. // if (pc->sz == DATA_MAX)
    65. // {
    66. // printf("通讯录已满,增加失败\n");
    67. // return;
    68. // }
    69. // //增加信息
    70. // printf("请输入名字:");
    71. // scanf("%s", pc->data[pc->sz].name);
    72. // printf("请输入年龄:");
    73. // scanf("%d", &(pc->data[pc->sz].age));
    74. // printf("请输入性别:");
    75. // scanf("%s", pc->data[pc->sz].sex);
    76. // printf("请输入电话:");
    77. // scanf("%s", pc->data[pc->sz].tele);
    78. // printf("请输入地址:");
    79. // scanf("%s", pc->data[pc->sz].addr);
    80. // //对sz增加,
    81. // pc->sz++;
    82. // printf("增加成功\n");
    83. //}
    84. void DestroyContact(Contact* pc)
    85. {
    86. free(pc->data);
    87. pc->data = NULL;
    88. pc->sz = 0;
    89. pc->capacity = 0;
    90. }
    91. //检查是否需要扩容
    92. void CheckCapacity(Contact* pc)
    93. {
    94. if (pc->sz == pc->capacity)
    95. {
    96. Peo* ptr = (Peo*)realloc(pc->data, (pc->capacity + DEFAULT_INC) * sizeof(Peo));
    97. if (ptr != NULL)
    98. {
    99. pc->data = ptr;
    100. pc->capacity += DEFAULT_INC;
    101. printf("增容成功\n");
    102. }
    103. else
    104. {
    105. perror("AddContact->realloc");
    106. return;
    107. }
    108. }
    109. }
    110. //增加联系人
    111. void AddContact(Contact* pc)
    112. {
    113. assert(pc);
    114. //增加容量
    115. CheckCapacity(pc);
    116. //增加信息
    117. printf("请输入名字:");
    118. scanf("%s", pc->data[pc->sz].name);
    119. printf("请输入年龄:");
    120. scanf("%d", &(pc->data[pc->sz].age));
    121. printf("请输入性别:");
    122. scanf("%s", pc->data[pc->sz].sex);
    123. printf("请输入电话:");
    124. scanf("%s", pc->data[pc->sz].tele);
    125. printf("请输入地址:");
    126. scanf("%s", pc->data[pc->sz].addr);
    127. pc->sz++;
    128. printf("增加成功\n");
    129. }
    130. //显示所有的联系人
    131. void ShowContact(const Contact* pc)
    132. {
    133. assert(pc);
    134. //判断通讯录内是否有信息
    135. if (pc->sz == 0)
    136. {
    137. printf("打印失败,通讯录为空\n");
    138. return;
    139. }
    140. //打印
    141. //打印标题
    142. printf("%-20s%-5s%-5s%-12s%-30s\n", "名字", "年龄", "性别", "电话", "地址");
    143. //打印信息
    144. int i = 0;
    145. for (i = 0; i < pc->sz; i++)
    146. {
    147. //打印每个人的信息
    148. printf("%-20s%-5d%-5s%-12s%-30s\n",
    149. pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
    150. }
    151. }
    152. //通过名字查找被删除人
    153. static int FindByName(Contact* pc, char* name)
    154. {
    155. assert(pc);
    156. int i = 0;
    157. for (i = 0; i < pc->sz; i++)
    158. {
    159. if (strcmp(pc->data[i].name, name) == 0)
    160. {
    161. return i;
    162. }
    163. }
    164. return -1;//找不到
    165. }
    166. //删除联系人
    167. void DelContact(Contact* pc)
    168. {
    169. assert(pc);
    170. //判断是否为空
    171. if (pc->sz == 0)
    172. {
    173. printf("删除失败,通讯录为空\n");
    174. return;
    175. }
    176. //查找是否存在此人
    177. char name[NAME_MAX];
    178. printf("请输入被删除人名字:\n");
    179. scanf("%s", name);
    180. //查找此人,存在返回位置,不存在返回-1
    181. int ret = FindByName(pc,name);
    182. if (ret == -1)
    183. {
    184. printf("要删除的人不存在\n");
    185. return;
    186. }
    187. //删除(覆盖)此人
    188. for (int i = ret; i < pc->sz - 1; i++) //sz-1 是防止执行体越界,如果要删除的是最后一个元素,通过sz--,直接不访问,
    189. {
    190. pc->data[i] = pc->data[i + 1];
    191. }
    192. pc->sz--;
    193. printf("删除成功\n");
    194. }
    195. //查找联系人
    196. void FindContact(Contact* pc)
    197. {
    198. assert(pc);
    199. //判断是否为空
    200. if (pc->sz == 0)
    201. {
    202. printf("查找失败,通讯录为空\n");
    203. return;
    204. }
    205. //查找此人,存在返回位置,不存在返回-1
    206. char name[NAME_MAX];
    207. printf("请输入被查找人名字:\n");
    208. scanf("%s", name);
    209. int ret = FindByName(pc, name);
    210. if (ret == -1)
    211. {
    212. printf("要查找的人不存在\n");
    213. return;
    214. }
    215. //显示出来
    216. printf("%-20s%-5s%-5s%-12s%-30s\n", "名字", "年龄", "性别", "电话", "地址");
    217. printf("%-20s%-5d%-5s%-12s%-30s\n",
    218. pc->data[ret].name, pc->data[ret].age, pc->data[ret].sex, pc->data[ret].tele, pc->data[ret].addr);
    219. }
    220. //修改联系人
    221. void ModifyContact(Contact* pc)
    222. {
    223. assert(pc);
    224. //判断是否为空
    225. if (pc->sz == 0)
    226. {
    227. printf("修改失败,通讯录为空\n");
    228. return;
    229. }
    230. //查找此人,存在返回位置,不存在返回-1
    231. char name[NAME_MAX];
    232. printf("请输入被修改人名字:\n");
    233. scanf("%s", name);
    234. int ret = FindByName(pc, name);
    235. if (ret == -1)
    236. {
    237. printf("要查找的人不存在\n");
    238. return;
    239. }
    240. //修改
    241. printf("请输入名字:");
    242. scanf("%s", pc->data[ret].name);
    243. printf("请输入年龄:");
    244. scanf("%d", &(pc->data[ret].age));
    245. printf("请输入性别:");
    246. scanf("%s", pc->data[ret].sex);
    247. printf("请输入电话:");
    248. scanf("%s", pc->data[ret].tele);
    249. printf("请输入地址:");
    250. scanf("%s", pc->data[ret].addr);
    251. printf("修改成功\n");
    252. }
    253. //目录排序
    254. //void qsort(void* base, size_t num, size_t size,
    255. // int (*compar)(const void*, const void*));
    256. int cmpare(const void* p1, const void* p2)
    257. {
    258. return strcmp(((Peo*)p1)->name, ((Peo*)p2)->name);
    259. }
    260. void SortContact(Contact* pc)
    261. {
    262. qsort(pc->data, pc->sz, sizeof(Peo), cmpare);
    263. }
    264. //保存信息到文件
    265. void SaveContact(Contact* pc)
    266. {
    267. FILE* pf = fopen("contact.txt", "wb"); //二进制读文件
    268. if (pf == NULL)
    269. {
    270. perror("SaveContact");
    271. return;
    272. }
    273. //写信息到文件
    274. int i = 0;
    275. for (i = 0; i < pc->sz; i++)
    276. {
    277. //fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pf);
    278. fwrite(pc->data + i, sizeof(Peo), 1, pf);
    279. }
    280. fclose(pf);
    281. pf = NULL;
    282. }

    contact.h

    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. #define NAME_MAX 20
    6. #define SEX_MAX 5
    7. #define TELE_MAX 12
    8. #define ADDR_MAX 30
    9. #define DATA_MAX 100
    10. #define DEFAULT_SZ 3 //默认容量
    11. #define DEFAULT_INC 2 //扩容量
    12. typedef struct Peo //个人信息结构体
    13. {
    14. char name[NAME_MAX];
    15. int age;
    16. char sex[SEX_MAX];
    17. char tele[TELE_MAX];
    18. char addr[ADDR_MAX];
    19. }Peo;
    20. typedef struct Contact
    21. {
    22. Peo* data;//存放数据
    23. int sz; //记录信息人数
    24. int capacity;//记录的是通讯录的当前容量
    25. }Contact;
    26. //初始化通讯录
    27. //void InitContact(Contact* pc);
    28. //动态版本的初始化
    29. void InitContact(Contact* pc);
    30. //增加联系人
    31. void AddContact(Contact* pc);
    32. //显示所有的联系人
    33. void ShowContact(const Contact* pc);
    34. //删除联系人
    35. void DelContact(Contact* pc);
    36. //查找联系人
    37. void FindContact(Contact* pc);
    38. //修改联系人
    39. void ModifyContact(Contact* pc);
    40. //排序联系人
    41. void SortContact(Contact* pc);
    42. //销毁通讯录
    43. void DestroyContact(Contact* pc);
    44. //保存信息到文件
    45. void SaveContact(Contact* pc);
    46. //加载文件信息到通讯录
    47. void LoadContact(Contact* pc);

    以上就是利用三章知识点结合做的小项目——通讯录;

    文中不足之处还望指点,感激不尽

     

  • 相关阅读:
    k8s 中的 ingress 使用细节
    C++ 指针与二维数组名
    Java Spring Beans.xml 里的 Bean 定义是如何被解析出来的
    0基础学习VR全景平台篇第119篇:利用蒙版航拍补天 - PS教程
    力扣45. 跳跃游戏 II
    ​ 详解Linux内核通信-proc文件系统
    关于NaN的一些总结
    若依的环境的部署以及系统的运行
    36二叉树-翻转二叉树
    【Globalmapper中文入门到精通系列实验图文教程】(附配套实验数据持续更新)
  • 原文地址:https://blog.csdn.net/m0_67367079/article/details/133857299