最近,帮别人改通讯录(链表+文件实现),大概是自己学的不够通透,曾经犯过的错误还是继续犯着。。。记录下来,警戒自己。
#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(这个程序里)
。。。