通讯录(链表+文件实现)+ 反思
2016-12-25 13:06
375 查看
最近,帮别人改通讯录(链表+文件实现),大概是自己学的不够通透,曾经犯过的错误还是继续犯着。。。记录下来,警戒自己。
反思:
1. 还是没习惯正确的错误调试,printf("%d",——LINE——);
2. 全局链表头变量,在函数中,需要定义一个临时指针指向他,但也要确保函数中不会改变临时指针的指向,如果那样了的话,会导致函数操作无效,仍然是NULL(这个程序里)
。。。
#include <stdio.h> #include <malloc.h> #include <stdlib.h> #include <string.h> #define LEN sizeof(person) struct stu /*定义结构*/ { char name[20]; /*姓名*/ char email[20]; /*电子邮件*/ char tel[20]; /*电话*/ char category[10]; /*类别*/ struct stu *next; /*结构指针*/ }; typedef struct stu person; person *first = NULL; /*令链表头为空*/ int num = 0; void Homepage(); /*主页函数*/ void add(); /*添加记录函数*/ void show(); /*显示记录函数*/ void del(); /*删除记录函数*/ void search(); /*查询记录函数*/ void modify(); /*修改记录函数*/ void quit(); /*退出程序函数*/ void elect(); /*执行程序函数*/ void read(); /*读取文件信息*/ void save(); int main() { while (1) { Homepage(); elect(); /*主函数中主页函数和执行程序函数一直在循环既主界面始终执行主页函数和执行程序函数*/ } return 0; } void Homepage() { system("cls"); /*清屏,每次返回到主页函数时先执行清屏指令*/ printf(" 欢迎使用通讯录查询系统 \n"); printf("\n ****通讯录**** \n"); printf("************************************************************\n"); printf("* 添 加 记 录 ----- (1) *\n"); printf("* 显 示 记 录 ----- (2) *\n"); printf("* 删 除 记 录 ----- (3) *\n"); printf("* 查 询 记 录 ----- (4) *\n"); printf("* 修 改 记 录 ----- (5) *\n"); printf("* 读 取 记 录 ----- (6) *\n"); printf("* 保 存 记 录 ----- (7) *\n"); printf("* 退 出 程 序 ----- (0) *\n"); printf("************************************************************\n"); printf(""); /*主页上的框架*/ printf("\n\n请输入功能号码:"); } void elect() { int i; scanf("%d", &i); while (i<0 || i>7) /*限制了输入的功能号码*/ { printf("输入的功能号码不正确,请重新输入:"); scanf("%d", &i); } switch (i) /*用switch来调用各函数*/ { case 1:add();break; case 2:show();break; case 3:del();break; case 4:search();break; case 5:modify();break; case 6:read();break; case 7:save();break; case 0:quit();break; } printf("按回车键返回"); getchar(); /*使exit返回到elect指令中而不是直接回到主函数中*/ getchar(); } void add() { int i; person *p1, *p2; /*定义两个指针p1用来开辟新结点,p2用来作上一个结点的尾*/ printf("\n*******************************\n"); printf("* 添加通讯录信息 *\n"); printf("*******************************\n"); p2 = first; /*先初始p2为空*/ if (p2 != NULL&&p2->next != NULL) { p2 = p2->next; /*这里是针对再一次或者多次使用功能1时,使接下来录进去的数据自动接在第一个结点后面,既p2从上一次的倒数第二个结点的尾部指向原来最后一个结点的尾部*/ } do { printf("按1继续添加信息"); scanf("%d", &i); if (i) /*如果非零继续执行*/ { if (num == 15) { printf("储存空间已满!"); /*这里用文字来限定储存空间只是形式上的,但实际上动态链表可以开辟的空间还很多很多,如果num超过15也是可以继续开辟的,只不过是有意无意罢了*/ } else { printf("\n请输入新添联系人信息(姓名;号码;分类(办公类,个人类,商务类);电子邮件)\n"); p1 = (person *)malloc(LEN); /*p1在开辟新的结点*/ scanf("%s%s%s%s", p1->name, p1->tel, p1->category, p1->email); /*新的结点数据的输入*/ p1->next = NULL; /*p1处于链表的最后端*/ if (first == NULL) { first = p1; } /*将表头附上第一个结点的地址*/ else { p2->next = p1; } /*使链表之间连接起来*/ p2 = p1; /*让p2随着p1往后移,并通过上面那条指令是整个链表连接起来*/ num++; /*计数*/ printf("\n添加成功!\n"); } } } while (i); /*当输入的值为0时结束循环*/ } void show() { person *p1; printf("\n*************************************\n"); printf("* 所有成员信息 *\n"); printf("*************************************\n"); p1 = first; /*此时first的地址已经变成了第一个结点的地址,现在p1就是第一个结点的地址,可以继续往下流动了*/ while (p1 != NULL) { printf("%s\t%s\t%s\t%s\n\n", p1->name, p1->tel, p1->category, p1->email); p1 = p1->next; /*链表再往下流动*/ } } void del() { int i, d; person *p1; person *p2; /*很常规的定义两个指针,一个做头,一个作尾*/ printf("\n*************************************\n"); printf("* 删除通信录信息 *\n"); printf("*************************************\n"); do { p1 = first; printf("%s\t%s\t%s\t%s\n\n", "姓名", "号码", "分类", "电子邮件"); for (i = 0;i<num;i++) { printf("%2d.%s\t%s\t%s\t%s\n", i + 1, p1->name, p1->tel, p1->category, p1->email); p1 = p1->next; } /*以上的程序完成的是信息的显示即之前录入的所有信息,主要的作用就是和下文有一个对比*/ printf("\n输入删除通信录信息成员的编号:"); scanf("%d", &d); if (d) { if (d>0 && d <= num) { if (d == 1) { p2 = first; first = first->next; /*这里的意义是,如果要删除第一个结点即表头的话,那么把头的地址改成第二个结点的地址,就可以把头给排出链表*/ free(p2); /*释放被排出结点的空间*/ } else { d--; d--; /*这里的两个d--是为了配合下面的while循环使得循环结束后p1落在要删除结点的前一个结点的位置*/ p1 = first; while (d--) { p1 = p1->next; /*循环结束后p1就落在了要删除结点的前一个位置*/ } p2 = p1->next; /*p2落在了要删除结点的位置上面*/ p1->next = p2->next; /*这时p1在要删除结点的前一个位置上,使得p1next指向要删除结点的后一个结点的地址,就把要删除的结点给排出了链表了*/ free(p2); /*释放空间*/ } num--; printf("\n删除成功!\n\n"); } else { printf("\n输入有误,重新输入:\n"); } } } while (d); /*结束循环*/ } void search() { char sh[10]; int count = 0; /*这里的count是用来计数的*/ person *p1; /*定义p1用来检索每一个结点*/ printf("\n*************************************\n"); printf("* 查找通信录信息 *\n"); printf("*************************************\n"); do { printf("A.办公类 B.个人类 C.服务类\n\n"); printf("请按类别进行查找,按Q返回:"); scanf("%s", sh); p1 = first; /*对p1进行赋值,使得p1指向第一个结点的地址*/ count = 0; if (sh[0] == 'A' || sh[0] == 'B' || sh[0] == 'C') /*限制输入的字符,如果不是ABC则打印错误*/ { if (sh[0] == 'A') { while (p1 != NULL) { if (!strcmp(p1->category, "办公类")) /*strcmp函数用来确定类别是否与所查询的类别相同,如果相同那么返回值为0,即!0为真,可以继续if语句*/ { if (!count) /*非零继续执行下面的步骤*/ { printf("搜索到以下相符的数据:\n\n"); printf("%s\t%s\t%s\t%s\n\n", "姓名", "电话号码", "类别", "电子邮件"); } printf("%s\t%s\t%s\t%s\n", p1->name, p1->tel, p1->category, p1->email); /*打印出符合条件的信息*/ count++; /*计数*/ } p1 = p1->next; /*进入下一个结点,使链表流动起来*/ } } else if (sh[0] == 'B') /*这里具体步骤同A一样*/ { while (p1 != NULL) { if (!strcmp(p1->category, "个人类")) { if (!count) { printf("搜索到以下相符的数据:\n\n"); printf("%s\t%s\t%s\t%s\n\n", "姓名", "电话号码", "类别", "电子邮件"); } printf("%s\t%s\t%s\t%s\n", p1->name, p1->tel, p1->category, p1->email); count++; } p1 = p1->next; } } else if (sh[0] == 'C') /*这里具体的步骤同A一样*/ { while (p1 != NULL) { if (!strcmp(p1->category, "商务类")) { if (!count) { printf("搜索到以下相符的数据:\n\n"); printf("%s\t%s\t%s\t%s\n\n", "姓名", "电话号码", "类别", "电子邮件"); } printf("%s\t%s\t%s\t%s\n", p1->name, p1->tel, p1->category, p1->email); count++; } p1 = p1->next; } } if (!count) /*如果一个相符的都没找到那么count为0即这条if语句可以继续下去*/ { printf("对不起!没有查找到相符的信息\n"); } else { printf("\n共%d个相符的信息", count); } /*很简单的计数*/ } else { printf("输入错误!\n"); } } while (sh[0] != 'Q'); /*当输入为Q的时候结束循环*/ } void modify() { int i, mdf; person *p1; printf("\n*************************************\n"); printf("* 修改通信录信息 *\n"); printf("*************************************\n"); do { p1 = first; printf("%s\t%s\t%s\t%s\n\n", "姓名", "电话号码", "分类", "电子邮件"); for (i = 0;i<num;i++) { printf("%2d. %s\t%s\t%s\t%s\n", i + 1, p1->name, p1->tel, p1->category, p1->email); p1 = p1->next; } /*这里没有多大意义,只是把之前录入的信息再打出来做一个对比*/ printf("\n输入对应编号1-%d修改该通讯录信息;输入0返回:", num); scanf("%d", &mdf); if (mdf) { if (mdf>0 && mdf <= num) { printf("\n请输入该通信录修改后的所有信息:\n(按“姓名 电话号码 分类(办公类/个人类/商务类) 电子邮件”的形式输入用空格隔开)\n\n"); printf("%d. ", mdf); p1 = first; mdf--; while (mdf--) { p1 = p1->next; } /*这里运用--的方法使得p1落在需要修改的结点处*/ scanf("%s%s%s%s", p1->name, p1->tel, p1->category, p1->email); printf("\n修改成功!\n\n"); /*重新输入数据覆盖之前的数据就可以了*/ } else { printf("\n您的输入有误,请重新输入:\n"); } } } while (mdf); /*结束循环*/ } #if 0 void save() { person *p1; FILE *fp = fopen("addresslist.dat", "w"); /*创建文件,并且写入数据*/ p1 = first; while (p1 != NULL) { fprintf(fp, "%s\t%s\t%s\t%s\n", p1->name, p1->tel, p1->category, p1->email); p1 = p1->next; /*数据的输出,这里很常规,只要文件的基本结构正确剩下的很好理解,注意点就是要在printf前加f*/ } fclose(fp); /*关闭保存*/ printf("\n数据已保存!\n\n"); } #endif #if 0 void save() { int i; person *p1; p1 = first; FILE *fp = fopen("addresslist.txt", "w"); /*创建文件,并且写入数据*/ for (i = 0;i<num;i++) { fwrite(p1, LEN, 1, fp); p1 = p1->next; } fclose(fp); } #endif #if 0 void read() { int i; FILE *fp; person *p1, *p2; p1 = (person *)malloc(LEN); p2 = first; fp = fopen("addresslist.txt", "a+"); for (i = 0;i < num;i++) { fread(p1, LEN, 1, fp); p2 = p1; if (p2->next == NULL)break; p1 = (person *)malloc(LEN); p2->next = p1; } p2->next = NULL; fclose(fp); printf("\n数据读取成功!"); } #endif #if 0 void read() { int i; FILE *fp; person *p1, *p2; p1 = (person *)malloc(LEN); p2 = first; fp = fopen("addresslist.txt", "rt"); for (i = 0;i < num;i++) { p1 = (person *)malloc(LEN); fread(p1, LEN, 1, fp); if (p2 == NULL)break; while (p2->next != NULL) { p2 = p2->next; } p2->next = p1; p1->next = NULL; } fclose(fp); printf("\n数据读取成功!"); } #endif void save() { FILE *fp; person *p = first; int i; if ((fp = fopen("addresslist.txt", "wt")) == NULL) { printf("cannot open file\n"); return; } while (p != NULL) { fprintf(fp, "%s %s %s %s\n", p->name, p->email, p->tel, p->category); p = p->next; } fclose(fp); } void read() { FILE *fp; person *p = first; person * newnode; int i = 1; if ((fp = fopen("addresslist.txt", "rt")) == NULL) { printf("cannot open file\n"); return; } while (i > 0) { newnode = (person *)malloc(LEN); i = fscanf(fp, "%s %s %s %s\n", newnode->name, newnode->email, newnode->tel, newnode->category); if (i == EOF) { free(newnode); newnode = NULL; break; } printf("%s %s %s %s\n", newnode->name, newnode->email, newnode->tel, newnode->category); if (p == NULL) { p = newnode; first = newnode; p ->next = NULL; } else { newnode->next = p->next; p->next = newnode; p = p->next; } } fclose(fp); return; } void quit() { exit(0); /*结束程序函数,用来结束当前的功能,返回到主页*/ }
反思:
1. 还是没习惯正确的错误调试,printf("%d",——LINE——);
2. 全局链表头变量,在函数中,需要定义一个临时指针指向他,但也要确保函数中不会改变临时指针的指向,如果那样了的话,会导致函数操作无效,仍然是NULL(这个程序里)
。。。
相关文章推荐
- 附带文件操作的通讯录,可以实现链表到文件的写入以及文件到链表的读取
- 用链表实现通讯录,并保存到文件
- C实现通讯录管理系统(亮点:纯链表实现、子串匹配,文件读写)
- 链表实现的粗略的通讯录
- 多文件编程动态开辟空间实现双向链表的应用 c++版
- 编写一个通讯录,功能与以前的诺基亚手机一样,可以通过键盘进行箭头的移动,回车进入选项,用“W”键代表手机手机左键,“backspace”键代表返回上一级。能够实现基本的人性化的增删改查及文件的导入导出
- K个有序链表共N个结点在O(NlgK)时间合并为一个新的有序链表实现文件C语言
- 二叉搜索树挂链表实现文件C语言
- 用读写文件方式实现通讯录
- Andorid使用自定义View实现通讯录,媒体文件等等的ListView的字母索引
- C语言课程设计通讯录链表实现
- 单链表实现个人通讯录管理助手
- 【头文件】c++实现链表
- C语言学习之用链表实现通讯录
- C语言学习之用链表实现通讯录
- 使用C++结合文件操作和链表实现学生成绩管理系统
- 通讯录(链表+文件)形式 C语言源代码
- 通讯录(链表+文件)形式 C语言源代码
- 二叉搜索树就地转双向链表二叉搜索树实现文件C++
- 链表的相关操作及简单通讯录系统的实现