2.(1)概述或引言
1.
本次系统程序全程采用dev—C++去开发,编译,调试。
2.
系统基本实现通信录的显示、添加、删除、查询、修改功能及基本实现联系人,电话号码,备注等条件去查询通讯录成员的方法以及拥有较良好的交互界面。
本次系统采用结构体加指针即链表实现。即大一练习作业
3
最开始时在main方法中使用局部指针进行删除操作时寻找节点位置无错但删除某位通讯录成员后,其之前节点的通讯录成员也再无法显示。最后在使用全局变量后问题解决。
编译后无任何运行错误。
2.(2)程序概要设计
2.2(1) 通讯录整体设计流程
整个系统可设计分为数据插入模块,
数据修改模块、数据删除模块、数据显示模块、数据查询模块。
2.2(2)
菜单函数:
菜单函数首先标出欢迎使用通讯录管理系,然后给出用户可选择的项目,用户可以通过选择该项前的代码来实现想要实现的功能。
2.2.(3)
添加函数:
添加函数通过创建动态内存空间来存储联系人信息的功能。
2.2.(4)
更改函数:
更改通讯录通过指针变量用另一个数据代替原先存在的数据,将新的数据存储在文件中。
2.2(5)
显示函数:
显示通讯录通过循环函数将全部的通讯录信息以此显示出来。
2.2.(6)
删除函数:
删除通讯录通过数组中该位置之后的数据存放位置向前移一位来实现删除功能。
2.2.(7)
查找函数:
查找通讯录通过数组位置查找相关信息实现查找功能。
2.2.(8)
退出函数:
在使用完通讯录后,按6键便会退出系统。
2.(3)程序详细设计
1.关于删除的函数的分析:
void delPerson(struct Person **contacts)//删除联系人函数
{struct Person *person;
struct Person *current;
struct Person *previous;
//先找到待删除的节点指针
person = findPerson(*contacts);
if(person == NULL)
{
printf("找不到该联系人!\n");
}
else
{
current = *contacts;
previous = NULL;
//current定位到待删除的节点
while(current != NULL &¤t !=person)
{
previous == current;
current = current->next;
}
if(previous == NULL)
{
//待删除的节点是第一个节点
*contacts = current;
}
else
{
//待删除的节点不是第一个节点
previous->next = current->next;
}
free(person)
通过对链表节点的思考,我找到了方法去解决链表节点的问题,就是建立三个节点指针去操作
- 修改待删除节点的上一个节点的指针,将其指向待删除节点的下一个节点。
- 释放待删除节点的内存空间。
尽管逻辑没错也删除了该节点信息但不清楚为什么会丢失之上的所有节点。
通过查询资料后修改代码如下:
void DeleteRecord_f()//删除联系人函数
{
char szSearch[40];
struct Person *strpFront;
strpFront=NULL;
strpCurrent=strpHead;
getchar();
printf("\n输入朋友的姓名以删除该记录:");
gets(szSearch);
while((strpCurrent!=NULL)&&(strcmp(strpCurrent->name,szSearch)!=0))
{
strpFront=strpCurrent;
strpCurrent=strpCurrent->next;
}
if(strpCurrent!=NULL)
{
printf("\n记录找到了\n");
printf("%s\n",strpCurrent->name);
printf("%s\n",strpCurrent->phoneName);
printf("%s\n",strpCurrent->note);
if(VerifyDel_f())
{
DelNode_f(strpFront);
printf("\n该记录已经删除!\n");
}
else
{
printf("\n该记录没有删除!\n");
}
}
else
{
printf("\n没有匹配的记录被删除!\n");
}
}
int VerifyDel_f() /*删除信息时要求予以确认*/
{
char chYesNo;
printf("你确定要删除吗?(Y/N)");
scanf("%c",&chYesNo);
if((chYesNo=='Y')||(chYesNo=='y'))
{
return(1);
}
else
{
return(0);
}
}
void DelNode_f(struct Person *strpFront)/*删除结点*/
{
if(strpCurrent==strpHead)
DelHead_f();
else
{
if(strpCurrent->next==NULL)
DelEnd_f(strpFront);
else
DelMid_f(strpFront);
}
}
void DelHead_f() /*删除头结点*/
{
strpCurrent=strpHead;
if(strpHead->next!=NULL)
strpHead=strpCurrent->next;
else
strpHead=NULL;
free(strpCurrent);
}
void DelEnd_f(struct Person *strpFront) /*删除尾结点*/
{
free(strpCurrent);
strpFront->next=NULL;
strpCurrent=strpHead;
}
void DelMid_f(struct Person *strpFront) /*删除链表中的结点*/
{
strpFront->next=strpCurrent->next;
free(strpCurrent);
strpCurrent=strpHead;
}
使用全局变量后问题解决了但本人仍不解为何局部逻辑一样时却会出现问题。
2.对读取文件函数的分析:
struct Person *LoadFile_f()/*从数据文件通讯录.txt中读取数据重建链表*/
{
struct Person *strpNew;
struct Person *contacts;
struct Person *p;
contacts=(struct Person *)malloc(sizeof(struct Person));
p=strpNew=contacts;
int a=0;
FILE *fpinfile;/*输入文件指针*/
if((fpinfile=fopen("通讯录1.txt","r"))==NULL)
{
printf("现在没有可用数据载入,链表为空\n");
system("cls");
}
else{
while(!feof(fpinfile))//位置很重要!!!
{
fscanf(fpinfile,"%s%s%s\n",strpNew->name,strpNew->phoneName,strpNew->note);
if(feof(fpinfile))
{break;
}
strpNew=(struct Person *)malloc(sizeof(struct Person));
p->next=strpNew;
p=strpNew;
} ;
p->next=NULL;
fclose(fpinfile);
return contacts;
free(strpNew);
通过对第十章的学习我明白了如何进行文件的读取的操作,而为了方便观看与修改我使用了文本模式去构建文件导入程序函数。在程序编译后尽管没有遭遇语法错误但却遇到逻辑问题
经过反复思考与调试,问题终于被暴露出来,来源于多构建了内存空间导致乱码出现。在内存排版正确后,乱码消失。
3.对将数据导入文件函数的分析:
导入文件函数要求将全部链表数据读入文件中。
void save_to_file(struct Person *contacts) //将联系人信息保存至文件
{
struct Person *current;
FILE *pfwrite;
pfwrite = fopen("通讯录1.txt", "w");
if (pfwrite == NULL)
{
printf("无法存储文件");
exit(EXIT_FAILURE);
}
current=contacts;
while(current!=NULL)
{
fprintf(pfwrite,"%s\n",current->name);
fprintf(pfwrite,"%s\n",current->phoneName);
fprintf(pfwrite,"%s\n",current->note);
current=current->next;
}
fclose(pfwrite);
}
此函数为文件指针的第一次使用,尽管逻辑相对简单,操作相对容易但由于第一次操作出现了语法错误,改正后函数实现了文件指针利用循环结构去循环链表数据完成导入文件。
4.对添加人员函数的分析:
添加函数要求构建动态空间将数据存储进链表保存。
void addPerson(struct Person **contacts)//添加人员函数
{
struct Person *person;
struct Person *temp;
person = (struct Person *)malloc(sizeof(struct Person));
if(person == NULL)
{
printf("内存分配失败!\n");
exit(1);
}
getInput(person);
//将person用头插法添加到通讯录里
if(*contacts != NULL)
{
strpHead=person;
temp = *contacts;
*contacts = person;
person->next = temp;
}
else
{
strpHead=person;
*contacts = person;
person->next = NULL;
}
}
void getInput(struct Person *person)//输入函数
{
printf("请输入姓名:");
scanf("%s",person->name);
printf("请输入电话:");
scanf("%s",person->phoneName);
printf("请输入备注:");
scanf("%s",person->note);
}
在我为数甚少的大型程序中是使用第二次链表与内存空间连接去编写程序使用与首次完成联系人数据存储进临时空间,当时做这个系统因为考虑数组人数有上限且本人不会扩容就选择动态链表不用考虑联系人数量,更好契合通讯录的需求,使用链表不需要改变内存地址,仅需要修改节点指针的指向以及节点的值即可,而数组大小固定,在对数组元素进行操作的过程中要移动该元素之后的所有元素的内存地址,操作起来比较麻烦。
2.(4)程序使用设计
添加功能的实现如下图:
查找功能的实现如下图:
实现多条件查询联系人。
更改功能的实现如下图:
删除功能的实现如下图:
显示功能的实现如下图:
退出功能如下图: