vc学生成绩管理系统-数据结构课程设计一、需求分析本演示程序用TC编写,主要用到的数据结构是线性表的链式存储结构。因为学生成绩管理系统需要经常更新,修改数据。根据这个特点我们采用了链式存储结构。它是一种动态存储结构。根据数据的特点,每一条记录包含多个属性,我们定义了一个结构体Student。开发目的如下:能对成绩进行录入和修改;能计算每个学生的平均成绩以及各门功课的平均成绩;能根据学号来查相应的学生成绩;能根据学生成绩的高低进行排名。1、 输入的形式和输入值的范围:学生的学号和姓名都为字符型,长度分别为6和5。输入的学号最多只能有5位,输入的姓名最多只能有4位。否则会出现乱码。学生的成绩在0-100之内。2、 输出的形式:以二进制的形式存储在你指定的硬盘里。3、 程序所能达到的功能:每一条记录包括一个学生的学号、姓名、3门成绩、平均成绩。输入功能:可以一次完成无数条记录的输入。显示功能:完成全部学生记录的显示。查找功能:完成按姓名查找学生记录,并显示。排序功能:按学生平均成绩进行排序。插入功能:按平均成绩高低插入一条学生记录。保存功能:将学生记录保存在任何自定义的文件中,如保存在:c:\score。读取功能:将保存在文件中的学生记录读取出来。有一个清晰美观界面来调用各个功能 4、 测试数据:num name Score1 Score2 Score310001 xing 88 98 8610002 wang 99 69 8710003 gong 78 99 89
二、概要设计为了实现上述程序功能,需要定义结构体的抽象数据类型和全局变量:ADT Student { 数据对象:D={ai|ai∈ElemSet,i=1,2,…,n,n≥0} 数据关系:R1={
|ai-1, ai∈D,i=2,…,n} 基本操作: IntiList(&L) 操作结果:构造一个空的线性表L GetElem(L,i,&e) 初始条件:线性表L已存在,1≤i≤ListLength(L)。 操作结果:用e返回L中第i个数据元素的值。 ListInsert(&L,I,e) 初始条件:线性表L已存在,1≤i≤ListLength(L)+1。 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1。 ListDelete(&L,I,e) 初始条件:线性表L已存在且非空,1≤i≤ListLength(L)。 操作结果:删除L中第i个数据元素,并用e返回其值,L的长度减1。}ADT Student下面是我们定义的结构体数组。整个链式存储结构中,所有在存储的记录(一个学生的信息)在物理位置上是不相邻的,是通过指针把各个结点联系在一起。每一个结点包含一条记录,包括学生的学号、姓名、三科的成绩、平均成绩、总分等等,存储下面所定义的结构体数组里面。这个结构体数组贯穿整个系统,可以说是整个系统的核心。typedef struct stu /*定义结构体数组用于缓存数据*/{char num[6];char name[5];int score[3];int sum;float average;int order;struct stu *next;}STUDENT;
三、详细设计整个系统除了主函数外,另外还有10个函数,实现八大功能:输入功能、显示功能、查找功能、排序功能、插入功能、保存功能、读取功能。各个函数的详细设计说明分别如下:1、 主函数 main()利用无限次循环for(;;)和switch()实现各函数的调用,系统根据输入的数字选项来调用相应的函数。2、 初始化函数 STUDENT *init()这是一个无参函数,里面只有一个语句,它的作用是使链表初始化,使head的值为NULL。比如:没有这个函数的话,在你没有输入任何数据的情况下,去执行显示功能的时候会显示一些乱码!3、 菜单选择函数 int menu_select();这是一个无参函数,主要实现“功能选择”的界面,在这个界面里有显示系统的九大功能,根据每个功能前面的序号进行选择,中间还显示系统当前的时间。等执行完每一个函数功能后,按任一键回到主界面也要通过这个函数来实现!4、 输入记录函数 STUDENT *create()这是一个无参函数,用来执行第学生成绩记录的输入,当学生为0时停止输入,函数结束后,带回一个指向链表头的指针head。算法:先声明一个首节点head,并将head->next设为NULL。每输入一个数据就声明一个新节点p,把p->next设为NULL,并且链接到之前列表的尾端。5、 显示记录函数 void print(STUDENT *head)这是一个不返回值的有参函数,形参为“链表头的指针”,负责对全部学生成绩记录的输出,不足之处就是不能对学生成绩进行分页显示。算法:先将p结点的指针指向第一个结点,将p结点(即第一个结点)的数据输出。然后再将p结点的指针指向p指针的的指针(即下一结点),将p结点(即第一结点)的数据输出。重复执行此步聚直到p指针指向NULL为止。6、 查找记录函数 void search(STUDENT *head)这是一个不返回值的有参函数,形参为“链表头的指针”,实现按学号对某个学生进行查找,并显示所查找到的记录。算法:采用线性查找法往下一个节点查找。输入所要查找的学生的学号s,设一个指针变量p,先指向第一个结点,当strcmp(p->name,s) && p != NULL时,使p后移一个结点,如果p!=NULL,输出p所指的结点。Status GetElem _L(LinkList L,int i,ElemType&e) {// L为带头结点的单链表的头指针// 当第i个元素存在时,其值赋给e并返回OK,否则返回ERRORP=L->next;j=1; //初始化,p指向第一个结点,j为计数器While(p&&jnext;++j;}If(!p||j>i)return ERROR; //第i个元素不存在e=p->data; //取第i个元素return OK;}
7、 删除记录函数 STUDENT *delete(STUDENT *head)这是一个有参函数,形参为“链表头的指针”,先输入要删除的学生记录的学号,找到后显示该学生信息,等确认后便可按“Y”进行删除。算法:从p指向的第一个结点开始,检查该结点中的num值是否等于输入的要求删除的那个学号。如果相等就将该结点删除,如不相等,就将p后移一个结点,再如此进行下去,直到遇到表尾为止。Status ListDelete _L(LinkList&L,int i,ElemType&e) {//在带头结点的单链线性表L中,删除第i个元素,并由e返回其值p=L;J=0;while(p->next&&jnext;++j;}if(!(p->next)||j>i-1)return ERROR; //删除位置不合理q=p->next;p->next=q->next; //删除并释放结点e=q->data;free(q);return OK;} //ListDelete_L
8、排序函数 STUDENT *sort(STUDENT *head)这是一个有参函数,形参为“链表头的指针”,按学生成绩的平均分高低进行排序,还可以显示名次。8、 插入函数 STUDENT *insert(STUDENT *head,STUDENT *new)这是一个有参函数,形参有两个,一个是“链表头的指针”,一个是“待插入指针”,按照原来成绩平均分的高低进行插入,插入后会重新进行排序,并返回。算法:先将学生的成绩按平均分由高分到低分进行排序,再插入一个新生的结点,要求按平均分的高低顺序插入。先用指针变量p0指向待插入的结点,p1指向第一个结点。如果p0->averageaverage,则待插入的结点不应插在p1所指的结点之前。此时将p1后移,并使p2指向刚才p1所指的结点。重复以上的步骤,直到p0->average>=p1->average为止。这时将p0指向的结点插到p1所指结点之前。但是如果p1所指的已是表尾结点,则p1就不应后移了。如果p0->average比所有结点的average都小,则应将p0所指的结点插到链表末尾。如果插入的位置既不在第一个结点之前,又不在表尾结点之后,则将p0的值赋给p2->,使p2->next指向待插入的结点,然后将p1的值赋给p0->next,使得p0->next指向p1指向的变量。如果插入位置为第一个结点之前,则将p0赋给head,将p1赋给p0->next。如果要插到表尾之后,应将p0赋给p1->next,NULL赋给p0->next。最后再调用排序的函数,将学生成绩重新排序.Status Listinsert _L(LinkList&L,int i,ElemType&e) {//在带头结点的单链线性表L中第i个位置之前插入元素ep=L;j=0;while (p&&jnext;++j;} //寻找第i-1个结点if(!p||j>i-1)return ERROR; //i小于1或者大于表长加1a=(Linklist)malloc(sizeof(LNode)); //生成新结点s->data=e;s->next=p->next;//插入L中return OK;}//ListInsert_L
10、保存数据到文件函数 void save(STUDENT *head)这是一个不返回值的有参函数,形参为“链表头的指针”,可以把学生记录保存在电脑上由自己任意命名的二进制文件。11、从文件读数据函数 STUDENT *load()这是一个不返回值的有参函数,形参为“链表头的指针”,根据输入的文件地址进行读取。
四、调试分析(1)刚开始没有那个初始化函数,程序运行后,没有输入任何数据就试着去执行显示功能,结果显示的是一些乱码。加入初始化函数后,这种现象也随之消失。(2)刚开始时,先把成绩按平均分排序,再插入一个学生的成绩,执行显示功能,虽然插入的学生的成绩能正常插入,但该学生的名次为0。后来,在插入成绩之后,调用排序函数,把所有成绩重新排序一次。(3)在输入函数中设了一个无限循环,可以输入无数个学生的成绩信息,当学号为0的时候则停止输入。(4)输入太多个学生的成绩时,屏幕显示不能控制为一页一页显示,所以为了方便起见,不要输入太多记录,十七左右为最佳。(5)在没有输入任何信息的情况下,去执行排序功能,最后显示有一个记录,学号、姓名为空白,成绩都为0,名次为1。(6)在输入选项时不能输入字母,否则会死循环,建议不要乱输字母。(7)刚开始时,显示所有记录时格式比较乱,字段和记录没能很好的对齐。很不好看。后来重新调整了一下显示格式,让学号字段(num)多几个空格才能够对齐。这样修改之后就美观了很多。(8)在输入测试数据的时候我们输入的6位的学号和5位的姓名。虽然我们定义了他们的字符串长度为6和5,但是我们没有考虑到字符串后面还有一个结束符/0。这样做的后果是出了很多莫名奇妙的错误。不能正常的删除数据,它老是提示找不到数据。
五、使用说明程序名为学生成绩管理系统.exe,运行环境为DOS。程序执行后显示166
vc学生成绩管理系统-数据结构课程设计Enter your choice<1~9>:在这里输入相应的数字,就会调用各个函数模块。实现对数据的录入、显示、查找、删除、排序、插入、保存、打开、退出操作。
六、总结 经过一个多星期的C语言课程设计,感觉自己收获不少。首先是:上课的时候链表没学好,但这个课程设计里面主要都是用链表,因为要达到这样的功能,使用链表相当方便,但不容易理解,所以在这方面我很了很多的时间看课本和参考课外书,使C语言的知识强化了不少。通过此次学生成绩管理系统的设计,使我对C语言程序设计有了深一步的了解,对系统设计及开发有了比较全面的思路。首先,通过对系统进行需求分析,确定各个功能函数;然后一个个具体地对他们进行编程,并且仔细检查看是否有错;在完成这一步之后才开始构造主函数,通过主函数把各个功能函数联系起来,编译修改错误和不足之处。其次,在做课程设计的过程中,发现了平时很多没有注意到的问题,例如:返回值函数和不返回值函数两者在主函数中的调用是不同的。在实际编程过程中会遇到很多不同的错误,有时候是一些很明显很细节的语法错误,这些可以通过编译来查找错误的所在,然而那些并不明显难以查找的隐性错误却真让人头痛,没有办法只好利用断点调试来一步步检查代码中的错误,这样为了找一个错误花费了大量的时间,但是找出错误之后的感觉却是非常“爽”的。同时我翻阅了大量的书籍,尽管大部分书都只是翻了一部分页码而已,却给了我一个非常有价值的经验。总之,此次系统设计给我们提供了一个既动手又动脑、自学、独立实践的机会,使我们养成了勤翻阅各种相关资料的习惯,将书本上的理论知识和实际有机地结合起来,锻炼了实际分析问题和解决问题的能力,提高了适应实际、实践编程的能力,为今后的学习和实践打下了良好的基础。
七、参考文献 1.严蔚敏、吴伟民.数据结构(C语言版)[M].北京:清华大学出版社.附件:小组成员任务分工表 课程设计进度安排表 学生成绩管理系统全部源代码 程序运行的部分截图
数据结构课程设计学生成绩管理系统(源代码)
/*头文件*/#include #include#include /*其它说明*/#include /*字符串函数*/#include /*内存操作函数*/#include /*字符操作函数*/#include /*动态地址分配函数*/#define LEN sizeof(STUDENT)typedef struct stu /*定义结构体数组用于缓存数据*/{char num[6];char name[5];int score[3];int sum;float average;int order;struct stu *next;}STUDENT;
/*函数原型*/STUDENT *init(); /*初始化函数*/int menu_select(); /*菜单函数*/STUDENT *create(); /*创建链表*/void print(STUDENT *head); /* 显示全部记录*/void search(STUDENT *head); /*查找记录*/STUDENT *delete(STUDENT *head); /*删除记录*/STUDENT *sort(STUDENT *head); /*排序*/STUDENT *insert(STUDENT *head,STUDENT *new); /*插入记录*/void save(STUDENT *head); /*保存文件*/STUDENT *load(); /*读文件*/
/*主函数界面*/main(){STUDENT *head,new;head=init(); /*链表初始化,使head的值为NULL*/for(;;) /*循环无限次*/ {switch(menu_select()) { case 1:head=create();break; case 2:print(head);break; case 3:search(head);break; case 4:head=delete(head);break; case 5:head=sort(head);break; case 6:head=insert(head,&new);break; /*&new表示返回地址*/ case 7:save(head);break; case 8:head=load(); break; case 9:exit(0); /*如菜单返回值为9则程序结束*/ } }}
/*初始化函数*/STUDENT *init(){return NULL; /*返回空指针*/}
/*菜单选择函数*/menu_select(){int n;struct date d; /*定义时间结构体*/getdate(&d); /*读取系统日期并把它放到结构体d中*/printf("press any key to enter the menu......"); /*按任一键进入主菜单*/getch(); /*从键盘读取一个字符,但不显示于屏幕*/clrscr(); /*清屏*/printf("********************************************************************************\n");printf("\t\t Welcome to\n");printf("\n\t\t The student score manage system\n");printf("*************************************MENU***************************************\n");printf("\t\t\t1. Enter the record\n"); /*输入学生成绩记录*/printf("\t\t\t2. Print the record\n"); /*显示*/printf("\t\t\t3. Search record on name\n"); /*寻找*/printf("\t\t\t4. Delete a record\n"); /*删除*/printf("\t\t\t5. Sort to make new a file\n"); /*排序*/printf("\t\t\t6. Insert record to list\n"); /*插入*/printf("\t\t\t7. Save the file\n"); /*保存*/printf("\t\t\t8. Load the file\n"); /*读取*/printf("\t\t\t9. Quit\n"); /*退出*/printf("\n\t\t Made by Hu Haihong.\n");printf("********************************************************************************\n");printf("\t\t\t\t%d\\%d\\%d\n",d.da_year,d.da_mon,d.da_day); /*显示当前系统日期*/do{ printf("\n\t\t\tEnter your choice(1~9):"); scanf("%d",&n); }while(n<1||n>9); /*如果选择项不在1~9之间则重输*/ return(n); /*返回选择项,主函数根据该数调用相应的函数*/}
lwfree.cn {printf("\nOut of memory."); /*输出内存溢出*/ return (head); /*返回头指针,下同*/ } printf("Enter the num(0:list end):"); scanf("%s",p->num); if(p->num[0]=='0') break; /*如果学号首字符为0则结束输入*/ printf("Enter the name:"); scanf("%s",p->name); printf("Please enter the %d scores\n",3); /*提示开始输入成绩*/ s=0; /*计算每个学生的总分,初值为0*/ for(i=0;i<3;i++) /*3门课程循环3次*/ { do{ printf("score%d:",i+1); scanf("%d",&p->score[i]); if(p->score[i]<0 || p->score[i]>100) /*确保成绩在0~100之间*/ printf("Data error,please enter again.\n"); }while(p->score[i]<0 || p->score[i]>100); s=s+p->score[i]; /*累加各门成绩*/ } p->sum=s; /*将总分保存*/ p->average=(float)s/3; /*先用强制类型转换将s转换成float型,再求平均值*/ p->order=0; /*未排序前此值为0*/ p->next=head; /*将头结点做为新输入结点的后继结点*/ head=p; /*新输入结点为新的头结点*/ } return(head); }
/* 显示全部记录函数*/
vc学生成绩管理系统void print(STUDENT *head){int i=0; /* 统计记录条数*/STUDENT *p; /*移动指针*/clrscr();p=head; /*初值为头指针*/printf("\n************************************STUDENT************************************\n");printf("-------------------------------------------------------------------------------\n");printf("| Rec | Num | Name | Sc1 | Sc2 | Sc3 | Sum | Ave | Order |\n");printf("-------------------------------------------------------------------------------\n");while(p!=NULL) { i++; printf("| %3d | %4s | %-4s | %3d | %3d | %3d | %3d | %4.2f | %-5d|\n", i, p->num,p->name,p->score[0],p->score[1],p->score[2],p->sum,p->average,p->order); p=p->next; }printf("-------------------------------------------------------------------------------\n");printf("**************************************END**************************************\n");}
/*查找记录函数*/void search(STUDENT *head){STUDENT *p; /* 移动指针*/char s[5]; /*存放姓名用的字符数组*/clrscr();printf("Please enter name for searching.\n");scanf("%s",s);p=head; /*将头指针赋给p*/while(strcmp(p->name,s) && p != NULL) /*当记录的姓名不是要找的,或指针不为空时*/ p=p->next; /*移动指针,指向下一结点*/ if(p!=NULL) /*如果指针不为空*/ {printf("\n*************************************FOUND************************************\n"); printf("-------------------------------------------------------------------------------\n"); printf("| Num | Name | sc1 | sc2 | sc3 | Sum | Ave | Order |\n"); printf("-------------------------------------------------------------------------------\n"); printf("| %4s | %4s | %3d | %3d | %3d | %3d | %4.2f | %-5d|\n", p->num,p->name,p->score[0],p->score[1],p->score[2],p->sum,p->average,p->order); printf("-------------------------------------------------------------------------------\n"); printf("***************************************END**************************************\n"); } else printf("\nThere is no num %s student on the list.\n",s); /*显示没有该学生*/}
/*删除记录函数*/STUDENT *delete(STUDENT *head){int n;STUDENT *p1,*p2; /*p1为查找到要删除的结点指针,p2为其前驱指针*/char c,s[6]; /*s[6]用来存放学号,c用来输入字母*/clrscr();printf("Please enter the deleted num: ");scanf("%s",s);p1=p2=head; /*给p1和p2赋初值头指针*/while(strcmp(p1->num,s) && p1 != NULL) /*当记录的学号不是要找的,或指针不为空时*/ {p2=p1; /*将p1指针值赋给p2作为p1的前驱指针*/ p1=p1->next; /*将p1指针指向下一条记录*/ }if(strcmp(p1->num,s)==0) /*学号找到了*/ {printf("**************************************FOUND************************************\n"); printf("-------------------------------------------------------------------------------\n"); printf("| Num | Name | sc1 | sc2 | sc3 | Sum | Ave | Order |\n"); printf("-------------------------------------------------------------------------------\n"); printf("| %4s | %4s | %3d | %3d | %3d | %3d | %4.2f | %-5d|\n", p1->num,p1->name,p1->score[0],p1->score[1],p1->score[2],p1->sum,p1->average,p1->order); printf("-------------------------------------------------------------------------------\n"); printf("***************************************END**************************************\n"); printf("Are you sure to delete the student Y/N ?"); /*提示是否要删除,输入Y删除,N则退出*/ for(;;)