一.通讯录思路
1.创建结构体类型,其中包含通讯录中联系人的信息;
该信息可以包括:姓名,年龄,性别,电话,地址等;
2.创建一个结构体存储通讯录;
3.写一个简易的菜单来表明通讯录的功能;
包括但不限于: 添加add,删除del,查找search,修改modigy,排序sort,打印通讯录print等;
4.通过函数来实现这些功能;
二.三个文件的建立
对于较复杂的一些代码,我们最好分文件写,这样可以使代码更清晰,可读性也更高;
在写通讯录的代码之前,我们需要先建立三个文件,分别是:
1.头文件 : contact.h (用于函数的声明等)
2.源文件 :contact.c (用于函数的实现)
3.源文件 : test.c (用于主函数的书写)
三.所需要使用的变量的创建(包含在头文件contact.h中)
为了后面方便改动,我们定义宏来确定一些大小;
代码:
1. //所需头文件的包含 2. #include <stdio.h> 3. #include <string.h> 4. #include <stdlib.h> 5. 6. //一些定义的表示大小的宏 7. #define MAX_NAME 20 8. #define MAX_SEX 10 9. #define MAX_TEL 20 10. #define MAX_ADDR 30 11. 12. #define DEFAULT_SZ 3 //默认通讯录容量 13. #define INC_SZ 2 //每次增容的大小 14. 15. 16. //定义每个联系人的信息 17. typedef struct peoinfo 18. { 19. char name[MAX_NAME]; //姓名 20. int age; //年龄 21. char sex[MAX_SEX]; //性别 22. char tel[MAX_TEL]; //电话 23. char addr[MAX_ADDR]; //地址 24. }peoinfo; 25. 26. 27. //存储每个联系人 28. typedef struct contact 29. { 30. peoinfo* data; //定义指针变量,方便后续动态内存开辟,进行通讯录扩容操作 31. int sz; //通过下标访问数组 32. int capacity; // 记录通讯录容量 33. }contact;
四.主函数的书写 (包含在 test.c 中)
1.首先要写个简易的菜单来展示通讯录功能;
2.然后利用 do ..... while结构实现通讯录的操作;
3.还要创建一个通讯录变量 contact con ;
4. 写一个函数 ( Innitcontact (函数的实现在 contact.c 中)(包含文件的读取)) 初始化通讯录变量 con (在此时进行动态内存开辟,使用 calloc/malloc 函数)
主函数代码:
1. //头文件的包含 2. #include "contact.h" 3. 4. 5. //菜单 6. void menu() 7. { 8. printf("*****************************************************************\n"); 9. printf("********** 1.add 2.del **********\n"); 10. printf("********** 3.search 4.modify **********\n"); 11. printf("********** 5.see 6.sort **********\n"); 12. printf("********** 0.exit **********\n"); 13. printf("*****************************************************************\n"); 14. 15. 16. } 17. //利用枚举变量使代码表达的意思更清晰 18. enum option 19. { 20. EXIT, //默认从0开始 21. ADD, //1 22. DEL, //2 23. SEARCH, //3 24. MODIFY, //4 25. SEE, //5 26. SORT //6 27. }; 28. 29. int main() 30. { 31. int input = 0; 32. contact con; //通讯录变量创建 33. //初始化联系人数组,包含从文件中读取联系人信息 34. Innitcontact(&con); 35. do 36. { 37. menu(); 38. printf("请选择:>"); 39. scanf("%d", &input); 40. switch (input) 41. { 42. case ADD: 43. addcontact(&con); //添加 44. break; 45. case DEL: 46. delcontact(&con); //删除 47. break; 48. case SEARCH: 49. searchcontact(&con); //查找 50. break; 51. case MODIFY: 52. modifycontact(&con); //修改 53. break; 54. case SEE: 55. printcontact(&con); //打印通讯录 56. break; 57. case SORT: 58. sortcontact(&con); //排序 59. break; 60. case EXIT: 61. savecontact(&con); //保存通讯录,将联系人信息写入文件中 62. destroycontact(&con); //销毁通讯录 63. printf("退出通讯录\n"); 64. break; 65. default: 66. printf("选择错误,重新选择\n"); 67. break; 68. } 69. } while (input); 70. return 0; 71. }
Innitcontact 函数:
1. //将文件中的联系人信息读入 2. void download(contact* pc) 3. { 4. FILE* pf = fopen("D:\\C\\c-code\\CONTACT\\CONTACT\\contact.txt", "r"); //以只读的方式打开文件 5. if (pf == NULL) //判断文件是否打开成功 6. { 7. perror("fopen"); 8. return; 9. } 10. 11. peoinfo tmp = { 0 }; 12. while (fread(&tmp, sizeof(peoinfo), 1, pf)) 13. { 14. inccapacity(pc); //增容函数 15. pc->data[pc->sz] = tmp; //将联系人信息写入 data 中 16. pc->sz++; //记录写入联系人的数量 17. } 18. fclose(pf); //关闭文件 19. pf = NULL; 20. } 21. 22. //动态初始化联系人 23. void Innitcontact(contact* pc) 24. { 25. pc->data = (peoinfo*)calloc(DEFAULT_SZ, sizeof(peoinfo)); //动态内存开辟,实现通讯录的动态 26. if (pc->data == NULL) //判断内存开辟是否成功 27. { 28. perror("Innitcontact"); 29. return; 30. } 31. pc->sz = 0; //初始化通讯录实时容量,也可用作下标 32. pc->capacity = DEFAULT_SZ; //初始化容量 33. download(pc); //加载文件中联系人的信息 34. }
五.通讯录功能的实现(在 contact.c 中)
1.添加联系人 addcontact
在添加联系人之前,我们先要判断通讯录是否已满,若已满则调用增容函数,之后在进行联系人信息的添加;
增容函数 inccapacity 代码:
1. //是否增容 2. void inccapacity(contact* pc) 3. { 4. if (pc->sz == pc->capacity) 5. { 6. printf("通讯录已满,开始增容\n"); 7. peoinfo* ptr = (peoinfo*)realloc(pc->data,(DEFAULT_SZ+INC_SZ)*sizeof(peoinfo)); //利用 realloc 进行内存的再次动态开辟,实现通讯录的增容 8. 9. if(ptr == NULL) //判断内存是否开辟成功 10. { 11. printf("增容失败\n"); 12. perror("inccapacity"); 13. return; 14. } 15. else 16. { 17. pc->data = ptr; //将扩容后的通讯录首地址赋给原来的通讯录 18. pc->capacity += INC_SZ; //容量增加 19. printf("增容成功\n"); 20. } 21. } 22. }
添加联系人函数 addcontact 代码:
1. //添加联系人 2. void addcontact(contact* pc) 3. { 4. int input = 0; 5. do 6. { 7. 8. printf("按1继续,按0返回:>"); //利用 do ... while 结构实现联系人的多次添加 9. scanf("%d", &input); 10. 11. switch (input) 12. { 13. case 1: 14. inccapacity(pc); //判断容量是否已满,若已满,则进行增容 15. printf("开始添加\n"); //联系人各种信息的录入 16. printf("请输入姓名:>"); 17. scanf("%s", pc->data[pc->sz].name); 18. printf("请输入年龄:>"); 19. scanf("%d", &(pc->data[pc->sz].age)); 20. printf("请输入性别:>"); 21. scanf("%s", pc->data[pc->sz].sex); 22. printf("请输入电话:>"); 23. scanf("%s", pc->data[pc->sz].tel); 24. printf("请输入地址:>"); 25. scanf("%s", pc->data[pc->sz].addr); 26. pc->sz++; //添加成功后,通讯录实时容量增加1 27. printf("添加成功\n"); 28. break; 29. case 0: 30. printf("返回\n"); 31. return; 32. break; 33. default: 34. printf("选择错误,重新选择\n"); 35. break; 36. } 37. } while (input); 38. }
2.删除联系人 delcontact
1.在删除前我们需要先判断通讯录中是否有数据,若没有则无法删除;
2.输入要删除的对象,所以我们需要写一个姓名的查找函数,返回其所在的下标,供后续删除使用;
3.删除联系人,即从返回的下标开始,使其之后的每一个元素向前移动一个位置;
查找函数 find 代码:
1. //查找联系人 2. int find(char tmp[], contact* pc) 3. { 4. int i = 0; 5. for (i = 0; i < pc->sz; i++) 6. { 7. if (strcmp(tmp, pc->data[i].name) == 0) //因为姓名是字符串,所以利用字符串比较函数 8. { 9. return i; //查找成功返回其下标 10. } 11. } 12. return -1; //失败则返回-1 13. }
delcontact 代码:
1. //删除联系人 2. void delcontact(contact* pc) 3. { 4. char tmp[MAX_NAME]; 5. int pos = 0, i = 0; 6. if (pc->sz == 0) //判断通讯录有无数据 7. { 8. printf("通讯录为空,无法删除\n"); 9. return; 10. } 11. while (1) 12. { 13. again: 14. printf("请输入要删除的人的姓名:>"); 15. scanf("%s", tmp); 16. pos = find(tmp, pc); //查找要删除人的下标 17. if (pos == -1) 18. { 19. printf("要删除的人不存在,重新输入\n"); 20. goto again; 21. } 22. else 23. break; 24. } 25. printf("开始删除\n"); 26. for (i = pos; i < pc->sz - 1; i++) //从返回的下标 pos 开始 ,之后的每个元素向前移动一位 27. { 28. pc->data[i] = pc->data[i + 1]; 29. } 30. pc->sz--; //删除成功即通讯录的实时容量减去1 31. printf("删除成功\n"); 32. }
3.查询联系人 searchcontact
1.在查询前需要判断通讯录中是否有数据,若无数据,则无法查询;
2.可以调用前面的查找函数;
3.查询到后打印该联系人信息,并提示查询成功;
searchcontact 代码:
1. //查找联系人 2. void searchcontact(contact* pc) 3. { 4. char name[MAX_NAME]; 5. int pos = 0; 6. if (pc->sz == 0) //判断通讯录中是否有数据 7. { 8. printf("通讯录为空,无法查询\n"); 9. return; 10. } 11. while (1) 12. { 13. again: 14. printf("请输入要查找人的姓名:>"); 15. scanf("%s", name); 16. pos = find(name, pc); //调用 fing 函数,并返回其下标 17. if (pos == -1) 18. { 19. printf("查无此人,重新查询\n"); //查询失败则继续 20. goto again; 21. } 22. else 23. break; 24. } 25. printf("查询成功\n"); 26. printf("%-20s\t%-5s\t%-10s\t%-20s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址"); //打印该联系人的信息 27. printf("%-20s\t%-5d\t%-10s\t%-20s\t%-30s\n", pc->data[pos].name, 28. pc->data[pos].age, 29. pc->data[pos].sex, 30. pc->data[pos].tel, 31. pc->data[pos].addr); 32. 33. }
4.修改联系人信息 modifycontact
1.判断通讯录中是否有数据,若无数据,则无法修改;
2.输入要修改人的姓名,调用函数 find ,返回其下标;
3.开始修改,即从新录入该联系人信息;
modifycontact 代码:
1. //修改联系人 2. void modifycontact(contact* pc) 3. { 4. if (pc->sz == 0) //判断通讯录是否有数据 5. { 6. printf("通讯录为空,无法修改\n"); 7. return; 8. } 9. char name[MAX_NAME]; 10. int pos = 0; 11. while (1) 12. { 13. again: 14. printf("请输入要修改的联系人的姓名:>"); 15. scanf("%s", name); 16. pos = find(name, pc); //返回要修改的联系人的下标 17. if (pos == -1) 18. { 19. printf("要修改的联系人不存在,重新输入\n"); 20. goto again; 21. } 22. else 23. break; 24. } 25. printf("开始修改\n"); //修改联系人信息 26. printf("请输入姓名:>"); 27. scanf("%s", pc->data[pos].name); 28. printf("请输入年龄:>"); 29. scanf("%d", &(pc->data[pos].age)); 30. printf("请输入性别:>"); 31. scanf("%s", pc->data[pos].sex); 32. printf("请输入电话:>"); 33. scanf("%s", pc->data[pos].tel); 34. printf("请输入地址:>"); 35. scanf("%s", pc->data[pos].addr); 36. printf("修改成功\n"); 37. }
5.打印通讯录 printcontact
这并不难,直接看代码:
1. //打印通讯录 2. void printcontact(contact* pc) 3. { 4. int i = 0; 5. printf("%-20s\t%-5s\t%-10s\t%-20s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址"); //打印的格式可以根据个人喜好来 6. for (i = 0; i < pc->sz; i++) 7. { 8. printf("%-20s\t%-5d\t%-10s\t%-20s\t%-30s\n", pc->data[i].name, 9. pc->data[i].age, 10. pc->data[i].sex, 11. pc->data[i].tel, 12. pc->data[i].addr); 13. } 14. }
6.排序通讯录 sortcontact
1.首先判断通讯录中的数据是否足以支持排序,若没有数据,或就1个数据,那么都无需排序;
2.利用排序算法,完成通讯录的排序;
3.需要有交换元素的步骤,那么就需要创建中间变量(在 contact.h 中创建)来实现交换;
4.该变量可以设置成结构体变量,成员包括与data 同类型的变量 sort ,既然与 data 同类型那么就需要对其初始化;
sort 变量的创建:
1. typedef struct sort 2. { 3. peoinfo* sort; 4. }sort;
sortcontact 代码:
1. //排序通讯录 2. void sortcontact(contact* pc) 3. { 4. if (pc->sz < 2) 5. { 6. printf("通讯录数据不足,无法排序\n"); //判断是否支持排序 7. return; 8. } 9. int i = 0, j = 0; 10. sort S; //变量的创建 11. S.sort= (peoinfo*)calloc(pc->capacity, sizeof(peoinfo)); //sort 的初始化 12. if (S.sort == NULL) //判断动态内存是否开辟成功 13. { 14. perror("sortcontact"); 15. return; 16. } 17. printf("开始排序\n"); 18. for (i = 0; i < pc->sz - 1; i++) //排序算法 19. { 20. for (j = i + 1; j < pc->sz; j++) 21. { 22. if (strcmp(pc->data[i].name, pc->data[j].name) > 0) 23. { 24. S.sort[i] = pc->data[i]; //交换元素 25. pc->data[i] = pc->data[j]; 26. pc->data[j] = S.sort[i]; 27. } 28. } 29. } 30. printf("排序成功\n"); 31. free(S.sort); //排序成功后释放所开辟的内存 32. S.sort = NULL; //将指针置空,防止使用野指针 33. }
六.退出通讯录 (即input==0时)
1.在退出通讯录之前需要保存通讯录的数据,写一个保存函数 savecontact;
2.保存好后销毁通讯录,写一个销毁函数 destroycontact;
3.退出通讯录;
保存函数 savecontact
1. //保存文件中的联系人信息 2. void savecontact(contact* pc) 3. { 4. FILE* pf = fopen("D:\\C\\c-code\\CONTACT\\CONTACT\\contact.txt", "w"); //以只写的方式打开文件 5. if (pf == NULL) //判断文件是否打开成功 6. { 7. perror("savecontact"); 8. return; 9. } 10. int i = 0; 11. for (i = 0; i < pc->sz; i++) 12. { 13. fwrite(pc->data + i, sizeof(peoinfo), 1, pf); //向文件中写入通讯录的数据 14. } 15. fclose(pf); //数据写完后,关闭文件 16. pf = NULL; 17. }
销毁函数 destroycontact
1. //销毁通讯录 2. void destroycontact(contact* pc) 3. { 4. pc->sz = 0; 5. pc->capacity = DEFAULT_SZ; //容量回复默认值 6. free(pc->data); //释放之前开辟的内存 7. pc->data = NULL; //指针置空,防止野指针的出现 8. }