二○一一~二○一二学年第二学期
电子与信息工程系课程设计报告书
课程名称:程序设计基础实践
班级:通信1112班
学号:1120119201
姓名:郭朝
指导教师:任建平
二○一二年二月
1.题目
《学生成绩管理系统》的设计与实现
2.要求
(1)整个系统均用C语言实现;
(2)利用指针、链表来实现学生成绩的数据结构设计;
(3)系统具有录入、显示、查询、修改、删除、排序、插入、保存、装载、帮助基本功能;
(4)系统的各个功能模块都用函数的形式来实现;
(5)可以将学生成绩信息保存在文件中。
(6)可以将学生信息从文件中读取出来。
3.功能
(1)每一条记录包括一个学生的学号、姓名、3门成绩、总成绩。
(2)输入功能:可以一次完成无数条记录的输入。
(3)显示功能:完成全部学生记录的显示。
(4)查询功能:完成按学号查找学生记录,并显示。
(5)删除功能:完成按学号删除学生信息。
(6)排序功能:按学生总成绩进行排序。
(7)插入功能:按总成绩高低插入一条学生记录。
(8)保存功能:将学生记录保存在任何自定义的文件中,如保存在:c:\score。
(9)装载取功能:将保存在文件中的学生记录读取出来。
(10)简单的帮助功能以及菜单
4.内容设计
系统设计了多个函数来完成各个不同的功能,同一个功能也可能由不同的函数来共同完成。下面将对各个函数逐一进行说明:
首先定义了结构体类型如下:
typedef struct student{
int num; //学号
char name[20]; //姓名
int score[3]; //三科成绩
int sum; //三科总分
int index; //为方便排序而设置的变量
struct student *next;//指针域
}STUDENT;
4.1主函数void main()
利用无限次循环while()和swithch()实现各函数的调用,系统根据输入的数字选项来调用相应的函数。
4.2菜单选择函数void menu()
这是一个无参函数,主要实现“功能选择”的界面,在这个界面里有显示系统的各大功能,根据每个功能前面的序号进行选择,中间还显示系统当前的时间。等执行完每一个函数功能后,按任一键回到主界面也要通过这个函数来实现!
4.3帮主函数void help()
这是一个无参函数,提供了本系统的相关信息以及在使用本系统时的一些相关的注意事项。
4.4输入记录函数STUDENT *creat()
这是一个无参函数,用来执行学生成绩记录的输入,当学号为0时停止输入,函数结束后,带回一个指向链表头的指针head。
算法:先声明一个首节点head,并将headt设为NULL。每输入一个数据就声明一个新节点p,并且链接到之前列表的。
N-S流程图如下:
head=NULL
p指向新的区域,同时存入数据
while(p->num)
p->next=head;head=p;
p指向新的区域,同时存入数据
释放p
return head;
4.5 数据显示函数void print(STUDENT *head)
这是一个不返回值的有参函数,形参为“链表头的指针”,负责对全部学生成绩记录的输出,不足之处就是不能对学生成绩进行分页显示。
算法:先将p结点的指针指向第一个结点,将p结点(即第一个结点)的数据输出。然后再将p结点的指针指向p指针的的指针(即下一结点),将p结点(即第一结点)的数据输出。重复执行此步聚直到p指针指向NULL为止。
N-S流程图如下:
p=head,使指向第一个结点
输出p所指向的结点
p指向一下个结点
当p指的不是表尾
4.6 信息查询,由findnum(),search()连个函数组成
4.6.1 查找函数STUDENT *findnum(int num,STUDENT *head)
根据输入的学号来查找结点,并返回该结点的首地址。利用while循环并且用if判断是否p->num==num,如果是则返回该结点首地址,否则执行p=p->next。若找不到该信息则返回NULL。
4.6.2 查找并显示信息函数void search(STUDENT *head)
本函数需要调用findnum()函数。
输入了学号赋给变量num
P=findnum(num,head)
P为空
是否
显示“信息不存在”输出该结点的相关信息
4.7 信息修改函数void setnum(int num,STUDENT *head)
本函数需要调用findnum()函数。先根据学号找到该结点,再让用户完整的输入修改后的信息。并执行
p->num=num;
strcpy(p->name,name);
p->score[0]=score1;
p->score[1]=score2;
p->score[2]=score3;这些赋值语句对该学号的学生信息进行修改。
4.8删除函数STUDENT *delete(int num,STUDENT *head)
利用函数findnum()找到该学号的学生信息并删除。N-S图如下:
是head==NULL 否
显示“空
信息”
for(p=head;num!=p->num&&p!=NULL;pre=p,p=p->next);循环体为空
num=p->num
是否
显示“不存在该信息”是p==head 否
head=p->
next
re->next=p->next
显示“删除成功!”
return head;
4.9排序操作由findiindex,length(),rewindindex();setindex()
sort()共同完成。
4.9.1 查找函数STUDENT *findindex(int index,STUDENT *head)
按index查找结点。N-S图如下:
STUDENT *p=head;
while(p!=NULL)
if(p->index==index) return p
return NULL
4.9.2 计算长度的函数int length(STUDENT *head)
先设一个计数变量s,给他赋初值0.再利用for(p=head;p!=NULL;p=p->nexrt) s++,计算链表的长度。结果返回s的值。
4.9.3 index“洗牌”函数void rewindindex(STUDENT *head)
首先定义一个变量i,使它的初值为1,然后利用循环for(p=head;p;p=p->next) s++来实现变量index的“洗牌”。
4.9.4信息修改函数void setindex(int index,STUDENT
*head,int num,char *name,int score1,int score2,int score3) 这个函数是专门为下面的冒泡排序而设置的,它的参数较多。先根据index来找到以head 为头结点的链表中的相关信息。然后利用赋值语句以对信息进行修改。需要调用前面的findindex()函数。
N-S图如下
p=findindex(index,head)
p->num=num;
strcpy(p->name,name);
p->score[0]=score1;
p->score[1]=score2;
p->score[2]=score3;
p->sum=score1+score2+score3;
4.9.5 排序函数STUDENT *sort(STUDENT *head)
本函数利用冒泡排序法对链表信息进行排序。
N-S图如下:
定义各个相关的变量
是(len=length(head))==0 否
返回NULL rewindindex(head)
for(i=0;i for(j=0;j findindex(j+1,head)->sum>findindex(j,head)->sum 是否 {tnum=p->num;strcpy(name,p->name);t1= p->score[0];t2=p->score[1];t3=p->score[2]; } setindex(j+1,head,pre->num,pre->name,pre ->score[0],pre->score[1],pre->score[2]); setindex(j,head,tnum,name,t1,t2,t3);完成第 j与j+1之间信息交换 返回head 4.10 插入函数STUDENT *insert(STUDENT *head,STUDENT *new) 这是一个有参函数,形参有两个,一个是“链表头的指针”,一个是“待插入指针”,按照原来成绩总分的高低进行插入,插入后会重新进行排序,并返回头指针。 算法:插入一个新生的结点,要求按总分的高低顺序插入。先用指针变量p0指向待插入的结点,p1指向第一个结点。如果p0->average p0->next,使得p0->next指向p1指向的变量。如果插入位置为第一个结点之前,则将p0赋给head,将p1赋给p0->next。如果要插到表尾之后,应将p0赋给p1->next,NULL赋给p0->next。最后再调用排序的函数,将学生成绩重新排序. N-S流程图如下: P1=head, p0=new 原来的链表是空表 是否 将p0所指当p0->sum 的结点作为点 唯一结点p2指向p1位置p1向后移一个结点 p0->sum>=p1->sum 是否 p1指向头结点p1->next=p0 是否p0->next=NULL head=p0 p2->next=p0 (插到表尾之后) p0 ->next=p1 p0->next=p1 (插到表头之前) (插到表中间) head=sort(head);(将成绩重新排序) 并返回head 4.11 保存数据函数void save(STUDENT *head) 这是一个不返回值的有参函数,形参为“链表头的指针”,可以把学生记录保存在电脑上由自己任意命名的二进制文件。 N-S流程图如下: 输入要保存记录的文件地址file 文件不能打开 否是 p=head; 输出一个出错信当p不为空时息,并返回菜单 fwrite(p,sizeof(STUDENT),1,fp);写信息 p=p->next; (指针后移) fclose(fp); (关闭文件) 4.12 数据装载函数STUDENT *load() 这是一个无参函数,将从文件读取的数据保存在新建立的链表中,返回新创建的链表的头首地址。 N-S流程图如下 打开文件失败 是否 返回菜单head=(STUDENT *)malloc(sizeof(STUDENT));读入数据fread(head,sizeof(STUDENT),1,fp); p0指向head P0->next非空 p1指向新的区域,读入数据。 P0->next=p1,p0=p1; return head; 关闭文件 5.调试分析 1.系统存在的一些不足之处:(1)在信息录入时,必须以0学号作为结束标志才能创建成功;(2)信息修改时,先输入学号查找结点,然后必须重新输入完整的信息才能修改成功;(3)不能区分不同学号的学生信息;(4)界面的显示还存在一些问题。 2.刚开始执行输入函数,按学号顺序输入十个学生的成绩,输完后执行显示功能,学生成绩记录是按学号的反顺序显示的,试着在其中增加一些语句,希望能把学号按正常顺序显示,但暂时没有成功,所以在输入成绩时只能按学号反顺序输入,最后就按学号正常顺序输出了。 3输入太多个学生的成绩时,屏幕显示不能控制为一页一页显示,所以为了方便起见,不要输入太多记录。 4.一开始输入的姓名过长时会使显示内容显得不整齐,将输入数函数部分的姓名输入长度改为20后就整齐了。 5.在使用第八或者第九项功能时,必须输入完整的文件名,包括文件路径以及文件的扩展名都要书写正确方可操作成功,否则无法打开文件并自动退回系统。 6.总结 本程序是使用链表设计的,整整花了将近两个星期,很有成就感!一开始由于对链表还不是特别熟悉,就又去查阅了一些其他资料,比如谭浩强的《C程序设计》以及严蔚敏的《数据结构》,感觉收获不少,又学到了一些新东西。 这次课程设计虽然花了我不少时间,但正是这些时间,让我见识到了C语言的重要性。这个学生成绩管理系统都是在自己知识范围内完成的,显得不是很成熟,但是还是挺实用的! 由于这是第一次进行设计,写文档,难免会写得不好! 以后有机会的话我想进一步学习C语言! 附录:源代码 #include #include #include #include //******************结构体类型定义*********************** typedef struct student{ int num; //学号 char name[20]; //姓名 int score[3]; //三科成绩 int sum; //三科总分 int index; //为方便排序而设置的变量 struct student *next;//指针域 }STUDENT; //创建链表,无参函数,结果返回链表头结点指针 STUDENT *creat() { STUDENT *p,*head; int i=0,j;//计数器变量 head=NULL; p=(STUDENT*)malloc(sizeof(STUDENT)); scanf("%d%s",&p->num,p->name); p->sum=0;p->index=i; for(j=0;j<3;j++){ scanf("%d",&p->score[j]); p->sum+=p->score[j];} while(p->num!=0) { p->next=head;head=p; p=(STUDENT*)malloc(sizeof(STUDENT)); scanf("%d%s",&p->num,p->name); p->sum=0;p->index=(++i); for(j=0;j<3;j++){ scanf("%d",&p->score[j]); p->sum+=p->score[j];} }//while控制信息录入,以0学号结束 printf("创建成功!\n"); free(p); return(head); } //显示学生信息,打印函数,空类型 void print(STUDENT *head) { STUDENT *p=head; int i; if(head==NULL) printf("空信息!"); else{ printf(" 学号姓名汉语高数"); printf(" 英语总分\n");//提示信息 do{ printf("%10d%10s",p->num,p->name); for(i=0;i<3;i++) printf("%10d",p->score[i]); printf("%10d\n",p->sum); p=p->next; }while(p!=NULL); } }//print() //**********信息查询函数,由findnum()和search()组成******** STUDENT *findnum(int num,STUDENT *head) //按学号查找结点,返//回查找到的结点地址,失败则返回空 { STUDENT *p=head; while(p!=NULL) {if(p->num==num) return p; p=p->next; } return NULL; } void search(STUDENT *head) //按节点查找并显示信息 { int i,num; STUDENT *p; printf("输入学号:\n"); scanf("%d",&num); if((p=findnum(num,head))==NULL) printf("不存在该信息!\n"); else{ printf(" 学号姓名汉语高数"); printf(" 英语总分\n");//提示信息 printf("%10d%10s",p->num,p->name); for(i=0;i<3;i++) printf("%10d",p->score[i]); printf("%10d\n",p->sum); } } //**********信息修改,找到要修改的学生信息,并作修改********** void setnum(int num,STUDENT *head) { STUDENT *p; char name[20]; int score1,score2,score3; if((p=findnum(num,head))==NULL) printf("不存在该信息\n"); else{ printf("输入修改后的完整信息:\n"); scanf("%d%s",&num,name); scanf("%d%d%d",&score1,&score2,&score3); p->num=num; strcpy(p->name,name); p->score[0]=score1; p->score[1]=score2; p->score[2]=score3; p->sum=score1+score2+score3; } } //*************删除学生信息,释放该节点,返回头节点********** STUDENT *delete(int num,STUDENT *head) { STUDENT *p,*pre; if(head==NULL) printf("空信息!\n"); else{ for(p=head;num!=p->num&&p!=NULL;pre=p,p=p->next); if(num==p->num) { if(p==head) head=p->next; else pre->next=p->next; printf("删除成功!\n"); free(p); } else printf("不存在该信息!\n"); return head; } return NULL; } //*****排序操作,由findindex()length()rewindindex()***** //*****************setindex()sort()组成***************** void rewindindex(STUDENT *head) //将链表中的index按链表顺序标号 { int i=0; STUDENT *p; for(p=head;p!=NULL;p=p->next) {p->index=i;i++;} } int length(STUDENT *head) //计算链表长度 { int s=0; STUDENT *p; for(p=head;p!=NULL;p=p->next) s++; return s; } STUDENT *findindex(int index,STUDENT *head) //按index查找结点,返回结点地址 { STUDENT *p=head; while(p) {if(p->index==index) return p; p=p->next; } return NULL; } void setindex(int index,STUDENT *head,int num,char *name,int score1,int score2,int score3)//修改节点数据 { STUDENT *p; p=findindex(index,head); {p->num=num; strcpy(p->name,name); p->score[0]=score1;p->score[1]=score2;p->score[2]=score3; p->sum=score1+score2+score3; } } STUDENT *sort(STUDENT *head)//按总分排序,返回首地址 { void rewindindex(STUDENT*); int i,j;//计数器变量 int tnum,t1,t2,t3;//作为交换的中间变量 char name[20];//作为交换的中间变量 int len;//测量长度变量 STUDENT *p,*pre; if((len=length(head))==0) return NULL; else{ rewindindex(head);//先将index"重洗" for(i=0;i for(j=0;j if(findindex(j+1,head)->sum>findindex(j,head)->sum) {p=findindex(j+1,head);pre=findindex(j,head); {tnum=p->num;strcpy(name,p->name);t1=p->score[0]; t2=p->score[1];t3=p->score[2];} setindex(j+1,head,pre->num,pre->name,pre->score[0],pre->score[1],pre->score[2]); setindex(j,head,tnum,name,t1,t2,t3); }//完成j与j+1之间的信息交换 return head; } } //*********信息插入函数insert()返回头结点的地址************** STUDENT *insert(STUDENT *head,STUDENT *ne) { STUDENT *p0=ne,*p1=head,*p2; if(head==NULL) {head=p0;head->next=NULL;} //空链表,直接将new作为头节点 else{ while(p0->sum {p2=p1;p1=p1->next;}//查找插入位置 if(p0->sum>=p1->sum) { if(p1==head) {head=p0;p0->next=p1;}//表头插入 else{ p2->next=p0; p0->next=p1; }//链表中间插入 } else{ p1->next=p0;p0->next=NULL; }//表尾部插入 head=sort(head);//排序 } return head; } //*******************数据保存************************* void save(STUDENT *head) //空类型,将信息保存在给定的文件名的文件中 { STUDENT *p; char file[20]; FILE *fp; printf("输入文件名:\n"); scanf("%s",file); if((fp=fopen(file,"wb"))==NULL) { printf("无法打开文件!\n"); exit(0); } for(p=head;p!=NULL;p=p->next) fwrite(p,sizeof(STUDENT),1,fp); fclose(fp); } //********************数据的装载*************************** STUDENT *load() //创建链表,将信息导入其中,返回头结点地址{ FILE *fp; STUDENT *head,*p0,*p1; char file[20]; printf("输入文件名:\n"); scanf("%s",file); if((fp=fopen(file,"rb"))==NULL) { printf("无法打开文件!\n"); exit(0); } head=(STUDENT*)malloc(sizeof(STUDENT)); fread(head,sizeof(STUDENT),1,fp); p0=head; while(p0->next) { p1=(STUDENT*)malloc(sizeof(STUDENT)); fread(p1,sizeof(STUDENT),1,fp); p0->next=p1;p0=p1; } return head; fclose(fp); } //**********************帮助函数******************** void help() { printf("1 本系统由通信工程1112班郭朝开发记录的数据是"); printf("学生成绩的相关信息\n"); printf("2 信息录入时,顺次输入学生的学号、姓名及三科"); printf("成绩,数据之间以空格、Tab或回车间隔\n"); printf("3 姓名及文件名要小于20个西文字符,文件名请写"); printf("完整\n"); } //*********************菜单显示函数******************** void menu() { system("cls"); printf("学生成绩信息管理系统\n"); printf("1 信息录入\n"); printf("2 信息显示\n"); printf("3 信息查询\n"); printf("4 信息修改\n"); printf("5 信息删除\n"); printf("6 成绩排序\n"); printf("7 信息插入\n"); printf("8 数据保存\n"); printf("9 信息装载\n"); printf("10 帮助\n"); printf("请选择\n"); } //**************************主函数********************* void main() { int x,i,num; STUDENT *head=NULL,*p; menu(); scanf("%d",&x); while(x) { switch(x) { case 1: printf("请输入信息,以0学号作为结束标志:\n"); head=creat(); break; case 2: print(head); break; case 3: if(head==NULL) printf("空信息!\n"); else search(head); break; case 4: if(head==NULL) printf("空信息!\n"); else {printf("输入要修改的学号:\n"); scanf("%d",&num); setnum(num,head); } break; case 5: printf("请输入要删除的学生学号:\n"); scanf("%d",&num); head=delete(num,head); break; case 6: head=sort(head); print(head); break; case 7: printf("请输入插入的信息:\n"); p=(STUDENT*)malloc(sizeof(STUDENT)); scanf("%d%s",&p->num,p->name); p->sum=0;p->index=0; for(i=0;i<3;i++){ scanf("%d",&p->score[i]); p->sum+=p->score[i]; } head=insert(head,p); break; case 8: if(head==NULL) printf("空信息!\n"); else save(head); break; case 9: head=load(); break; case 10: help(); break; } printf("按任意键继续\n"); getchar();getchar(); menu(); scanf("%d",&x); } }