C语言课程设计学生通讯录.docx
- 文档编号:20177983
- 上传时间:2023-04-25
- 格式:DOCX
- 页数:28
- 大小:230.92KB
C语言课程设计学生通讯录.docx
《C语言课程设计学生通讯录.docx》由会员分享,可在线阅读,更多相关《C语言课程设计学生通讯录.docx(28页珍藏版)》请在冰豆网上搜索。
C语言课程设计学生通讯录
C语言程序设计课程设计报告
题目:
学生通讯录
学院计算机学院_______
专业___计算机科学与技术___
年级班别____09届5班_________
学号
学生姓名_______________
指导教师_______________
成绩____
2010年1月
一、设计目的
培养学生综合应用所学专业知识及计算机知识的能力,训练和提高软件开发技能。
在教师的指导下以课程设计为中心,独立地完成从可行性分析,需求分析,软件设计,编码到软件测试运行的软件开发全过程。
达到以下几点目标:
深化已学的知识,完成从理论到实践的转化;提高分析和解决实际问题的能力;培养"开拓创新"能力
二、总体设计(程序设计组成框图、较复杂函数的流程图)
1、程序设计组成框图:
2、较复杂函数流程图:
revise()修改函数:
N
Y
a=Ya=N
a为其它
b=1
b=2
三、思路
(1)我先是列出了函数目录,清楚程序需要什么功能以及菜单该如何设置,然后就做菜单框架,菜单框架的思路如下:
1.输出主菜单,输入数字并返回选择的数字
2.输出分菜单,使用分菜单菜单,通过switch(主菜单)实现,从主菜单中得到输入的数字,每一步case跳到各自的分支函数,运行完分函数后返回switch继续选择case目录
3.最后一个case选项用来跳出分菜单,返回第一步
(2)然后就是对各个函数的初步理解,此时思路确定为通过指针变量的移动和结构体的应用添加修改数据,列出了一下几个需要用到的函数名称并进行声明和列出程序的头文件和需要用到的宏定义和两个全局变量:
#include
#include
#include
#include
#include
#defineNULL0
#defineLENsizeof(structaddress)
intmainmenu(void);
intselectmenu(void);
voidrun1(intb);
voidrun2(intc,ADDR*head);
ADDR*creat(void);
ADDR*add_record(ADDR*head);
ADDR*delete_record(ADDR*head);
voidfind_record(ADDR*head);
voiddisplay(ADDR*head);
ADDR*sort(ADDR*head);
ADDR*load(ADDR*head);
ADDR*save(ADDR*head);
ADDR*revise(ADDR*head);
intn=0;
ADDR*head;
(3)最后就是完善函数,在单文件中运行调试,逐渐地修改函数。
四、详细设计(模块功能说明(如函数功能、入口及出口参数说明,函数调用关系描述等))
1、增加数据函数add_record()
声明ADDR*add_record(ADDR*head),以头指针为形参,并返回头指针。
函数内定义结构体指针变量p1、p2。
先为p1开辟一段空间,这段空间是一个这个结构体的结点的空间,提示用户循环输入名字和电话号码,并限制输入的号码为8位或11位,否则返回重新输入电话号码。
当指针变量p1得到一段数据后就将它插入当前链表的首部,通过p1->next=head;head=p1;来实现,每次输入一段数据后全局变量n就自加1,显示当前有n个结点。
当用户输入的名字为0时跳出循环,调用显示函数display(),在屏幕上输出当前数据个数以及数据。
最后返回头指针给函数。
2、查询记录函数find_record()
声明voidfind_record(ADDR*head),为空类型,以头指针作为形参。
定义字符数组name2,整形变量a、b,结构体指针变量p。
提示用户输入想要查找的记录中的名字,如果头指针为空,提示用户“记录为空”,并提示“输入任意键继续”,跳出此函数,否则进入循环,另指针p指向链表表头,通过p指针后移查找和name2相同的p1->name,直到p->next为空,如果没有相同的记录,就提示用户“这里没有想找的记录”,并提示“输入任意键继续”,跳出此函数,如果有就输出用户查找的记录,并提示“输入任意键继续”,跳出此函数。
3、修改记录函数revise()
声明ADDR*revise(ADDR*head),以头指针为形参,并返回头指针。
显示现在头指针中的所有记录,提示用户输入想要修改的记录的名字,进入循环查找,找出记录并输出,询问“是否想修改它”,并提示输入Y或者N,如果输入Y,提示输入新的记录,最后显示修改后的全部数据,如果输入N,询问用户“下一步想干什么”,列出选择“1、再次修改2、返回分菜单”,如果输入1,再次调用这个修改记录函数,如果输入2,跳出此函数,返回二级菜单,如果输入其他,则继续输入。
最后返回头指针给函数。
4、删除记录函数delete_record()
声明ADDR*delete_record(ADDR*head),以头指针为形参,并返回头指针。
定义结构体指针变量p1、p2,字符数组name1,并令p1、p2指向链表表头。
显示现在头指针中的所有记录,提示用户输入想要删除的记录的名字name1,如果头指针为空,提示为空并跳出此函数,如果不空,通过结构体指针p1后移查找出结构体内名字与用户输入的相同的记录,每一次p1的后移p2都跟在p1后面一个结点,如果第一个数据就为查找的内容,就令链表头指针指向其下一个结点,否则将p1的下一个结点作为p2的下一个结点,从而实现删除当前的p1结点,输出“用户删除的记录被删除,删除成功”,如果循环结束都找不到相同的名字记录,输出“name1查找不到”,最后输出当前删除后或是未删除过的全部数据,返回头指针给函数。
5、链表排序函数sort()
声明ADDR*sort(ADDR*head),以头指针为形参,并返回头指针。
定义结构体指针变量p1、p2、p0、p,整形变量i、j,通过指针调换的方法和冒泡法进行排序。
先进行头两个结点的排序,通过比较,将较大的放在前面,通过p1和头指针的交换实现。
然后进行后面结点的每两个的排序,将p0、p2、p1分别分配到相近的三个结点,当p2中的名字顺序比p1大时,调换p2和p1,将p2指向的结点赋给p0所指向,p1指向结点赋给p2所指向,p2赋给p1所指向,完成此步骤后p0、p2、p1全部后移,知道p1或p2指向空时结束。
提示用户“排序成功”,并输出当前排序后的数据记录,并保留屏幕,“输入任意键退出”,返回头指针给函数。
6、保存文件记录函数save()
声明ADDR*save(ADDR*head),以头指针为形参,并返回头指针。
定义文件指针fp,结构体指针变量p,字符数组filename。
提示用户输入文件名和文件保存路径,以w格式打开一个文本文件,如果无法打开,将提示“无法打开文件”并退出。
用fprintf输出链表数据到文件中,提示用户“保存成功,输入任意键退出”,关闭文件指针fp,返回头指针给函数。
7、读取文件记录函数load()
声明ADDR*load(ADDR*head),以头指针为形参,并返回头指针。
定义文件指针fp,结构体指针变量p、p0,字符数组filename。
提示用户输入文件名和文件读取的路径,以r格式打开这个文本文件,如果无法打开,将提示“无法打开文件”并退出。
用fscanf读取文本文件中的数据,指针p一边读取一边开辟空间,没想向后移一段先开辟一段空间,再将数据读入。
读取完成后自动输出读取到的所有数据并存在头指针head中,提示“输入任意键继续”,关闭文件指针fp,返回头指针给函数。
8、显示记录函数display()
声明voiddisplay(ADDR*head),函数为空类型,以头指针为形参。
定义结构体指针变量p。
通过指针p的结点后移,逐个输出链表的数据,直到p的下一个结点为空,最后就输出当前结点,输出“输入任意键继续”,退出函数。
9、主菜单使用函数run1()
声明voidrun1(intb),函数为空类型,以整形为形参。
定义整形变量m,结构体指针变量head。
将之前输入的整形数字b作为形参进入switch选择:
b为1,调用selectmenu函数和run2函数并结束循环;b为2,调用load函数和selectmenu函数和run2函数并结束循环;b为3,输出“再见!
”,保留屏幕,输入任意键退出系统;b为其它,继续输入。
10、分菜单使用函数run2()
声明voidrun2(intc,ADDR*head),函数为空类型,以整形和头指针两个为形参。
定义整形变量d。
将之前输入的整形数字c作为形参进入switch选择:
c为1,调用add_record函数和selectmenu函数和run2函数并结束循环;c为2,调用find_record函数和selectmenu函数和run2函数并结束循环;c为3,调用revise函数和selectmenu函数和run2函数并结束循环;c为4,调用delete_record函数和selectmenu函数和run2函数并结束循环;c为5,调用sort函数和selectmenu函数和run2函数并结束循环;c为6,调用save函数和selectmenu函数和run2函数并结束循环;c为7,调用mainmenu函数和run1函数并结束循环;c为其它则继续输入。
五、调试与测试:
调试方法,测试结果的分析与讨论,测试过程中遇到的主要问题及采取的解决措施
先做好菜单,实现了一级菜单和二级菜单之间的跳转,再按顺序地做好一个个函数,在记事本中编辑函数并保存,在TurboC++里调试修改并保存,从每次测试的结果中找到页面美观方面的缺陷和函数的出错,还有多余的或是缺少的都在调试中一目了然、
在用指针法排序和文件的读取那里总得不到我想要的结果,我百思不得其解,后来拿出草稿纸一步步地分析,发现是指针引用错误,有的是最后没有赋空,有的是步骤不完整,仅仅是将后面的排序了,忽略了前面两个,有得是结构体指针走过头了,输出时就会多出一段乱码,有的又是根本连文件都读取不了。
我自己在不断地修改,解决了不少难关,但文件读取那个函数还是会出错,后来和同学讨论,引入了多一个指针和修改了函数形参,增加了个结构体指针变量,跟着原来那个指针变量走,将最后那个结点给删了。
主要的问题都是增加数据函数和排序函数和读取函数,增加数据和排序都是我自己硬想出来的,步骤就挺复杂的,但是还是实现了相应的功能,读取函数同学和我一起讨论和调试了很久,最后我引入另一个变量将最后结点删掉才解决问题。
六、源程序清单和执行结果:
清单中应有足够的注释
源程序清单:
#include
#include
#include
#include
#include
#defineNULL0//宏定义
#defineLENsizeof(structaddress)
typedefstructaddress//*通讯录中记录的结构
{charname[15];
chartel[20];
structaddress*next;
}ADDR;
intn=0;//全局变量
ADDR*head;
intmainmenu(void);//函数声明
intselectmenu(void);
voidrun1(intb);
voidrun2(intc,ADDR*head);
ADDR*creat(void);
ADDR*add_record(ADDR*head);
ADDR*delete_record(ADDR*head);
voidfind_record(ADDR*head);
voiddisplay(ADDR*head);
ADDR*sort(ADDR*head);
ADDR*load(ADDR*head);
ADDR*save(ADDR*head);
ADDR*revise(ADDR*head);
ADDR*revise(ADDR*head)//修改记录函数
{intb,d;
ADDR*p,*p1;
charname3[20],a;
printf("\nNow,These%drecordsare:
\n",n);
printf("name\t\ttel\n");
p1=head;
if(head!
=NULL)//输出现在链表中的所有数据记录
do
{printf("%-20s%-20s\n",p1->name,p1->tel);
p1=p1->next;
}while(p1!
=NULL);
printf("\n\nInputthenamewhichyouwanttorevise:
\n");
scanf("%s",name3);
for(p=head;strcmp(p->name,name3)!
=0;p=p->next){}//将指针p移动到与输入相同的名字记录
printf("Yourrecordis:
\n%s%s\n",p->name,p->tel);
printf("Areyouwanttoreviseit?
(Y/N)\n");
getchar();
a=getchar();
if(a=='Y')
{printf("\n\nInputyourrecords:
\n");
printf("\nInputnewname:
");
scanf("%s",p->name);
printf("Inputnewtel:
");
scanf("%s",p->tel);
display(head);//调用显示函数,显示修改后的所有数据结果
}
elseif(a=='N')
{printf("Whatyouwanttodonext?
\n");
printf("1.Reviseitagain.\n2.Backtothemenu.\n");
scanf("%d",&b);
switch(b)
{case1:
revise(head);break;//调用本函数
case2:
getchar();d=selectmenu();run2(d,head);break;}
}
returnhead;
}
ADDR*add_record(ADDR*head)//*增加链表数据
{ADDR*p1,*p2;
if(head!
=NULL)
{while
(1)
{clrscr();
p1=(ADDR*)malloc(LEN);//开辟一段空间给p1
printf("\n\nInputyourrecords:
(input0toback)\n");
printf("\nInputnewname:
");
scanf("%s",p1->name);
if(*p1->name=='0')gotoend2;//p1->name是字符数组类型,0要加单引号,以此跳出循环
printf("Inputnewtel:
");
scanf("%s",p1->tel);
if(strlen(p1->tel)!
=8&&strlen(p1->tel)!
=11)//字符个数(电话号码)必须为8或11位
{printf("Inputerror!
Pleaseinputanothernewtelin8or11numbers:
");
scanf("%s",p1->tel);}
p1->next=head;
head=p1;
n=n+1;//结点的个数,记录的条数
printf("\n\n");
}
}
else
{while
(1)
{clrscr();
n=n+1;
if(n==1)
{clrscr();
p1=(ADDR*)malloc(LEN);
printf("\n\nInputyourrecords:
(input0twicetoback)\n");
printf("\nInputnewname:
");
scanf("%s",p1->name);
if(*p1->name=='0')break;
printf("Inputnewtel:
");
scanf("%s",p1->tel);
printf("\n\n");head=p1;p1->next=NULL;
}
elsep2->next=p1;
p2=p1;
p1=(ADDR*)malloc(LEN);
printf("\n\nInputyourrecords:
(input0twicetoback)\n");
printf("\nInputnewname:
");
scanf("%s",p1->name);
if(*p1->name=='0')gotoend2;
printf("Inputnewtel:
");
scanf("%s",p1->tel);
printf("\n\n");
}
}
end2:
p2->next=NULL;//链表尾赋空
display(head);
return(head);
}
ADDR*delete_record(ADDR*head)//*删除记录
{ADDR*p1,*p2,*p;
charname1[15],a;
printf("\nNow,These%drecordsare:
\n",n);
printf("name\t\ttel\n");
p=head;
if(head!
=NULL)//输出现在链表中所有的数据
do
{printf("%-20s%-20s\n",p->name,p->tel);
p=p->next;
}while(p!
=NULL);
printf("\n\nInputthenamewhichyouwanttodelete:
\n");
scanf("%s",name1);
if(head==NULL)
{printf("\nlistnull!
\n");
gotoend;}
p1=head;
while(strcmp(name1,p1->name)!
=0&&p1->next!
=NULL)//当输入的名字和链表中的名字相同或指针指向空就结束循环
{p2=p1;
p1=p1->next;}//将指针移到名字相同结点
if(strcmp(name1,p1->name)==0)
{if(p1==head)//如果是第一个相同
head=p1->next;//头指针后移
elsep2->next=p1->next;//删掉当前结点p1
printf("delete%ssuccess!
\n",name1);
n=n-1;}//结点数(记录段)减少1
elseprintf("%scan'tbeenfound!
\n",name1);
end:
{display(head);
return(head);
}
}
voidfind_record(ADDR*head)//*查询记录
{charname2[15];
ADDR*p;
inta,b;
printf("\nInputthenamewhichyouwanttofind:
\n");
scanf("%s",name2);
if(head!
=NULL)
{for(a=1,b=1,p=head;p->next!
=NULL;p=p->next,a++)//a是总结点数
{if(strcmp(p->name,name2)==0)//查找到相同的记录并输出
printf("\nYourrecordis:
\n%s%s",p->name,p->tel);
elseb++;//当没有相同的记录,每次循环都会进行这一步
}
if(a==b)printf("\nThereisnotherecordyouwant");
}
elseprintf("\nYourrecordisempty\n");
printf("\nPressanykeytocontinue....");
getchar();//接手前面scanf里的回车键
getchar();//保留屏幕
}
voiddisplay(ADDR*head)//显示记录
{ADDR*p;
printf("\nNow,These%drecordsare:
\n",n);
printf("name\t\ttel\n");
p=head;
if(head!
=NULL)
do
{printf("%-20s%-20s\n",p->name,p->tel);
p=p->next;//指针p后移
}while(p!
=NULL);
printf("Pressanykeytocontinue...");
getchar();
getchar();
}
ADDR*sort(ADDR*head)//*链表排序
{ADDR*p1,*p2,*p0,*p;
inti,j;
for(p1=head,i=1;p1->next!
=NULL;p1=p1->next,i++){}//数出总共的结点数i
for(j=0;j
{p1=head->next;
if(strcmp(head->name,p1->name)>0)//对链表前两个数据进行排序
{head->next=p1->next;
p1->next=head;
head=p1;
}
if(p1->next!
=NULL)
for(p0=head;p1->next!
=NULL;)
{p2=p0->next;
p1=p2->next;
if(p2->next==NULL)break;//p2指向为空跳出循环
if(p1->next==NULL)break;//p1指向为空跳出循环
if(strcmp(p2->name,p1->name)>0)
{p0->next=p2->next;//p1和p2的交换
p2->next=p1->next;
p1->next=p2;
}
p0=p0->next;//指针后移,逐个进行排序
}
}
printf("Sortsuccess!
\n");
printf("\nNow,These%drecor
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言 课程设计 学生 通讯录