成绩管理系统
一、实验名称:成绩管理系统
二、实验学时:4 学时
三、实验目的:
- 熟悉并掌握 C 语言程序设计的步骤。
- 掌握 C 语言中函数、结构体、数组等知识和其用法。
- 设计小型成绩管理系统,要求实现简单的功能,一门课程的成绩管理系统包括学生成绩的录入、查询、列表等功能。
四、实验内容:
要求:
利用结构数组存储所有学生信息,最多 100 个学生。编写学生成绩管理系统,其中学生的信息有学号、姓名(汉语拼音)和分数。
完成下面函数:
1、输入学生信息函数 AddStd 实现添加学生相关信息;
2、按学号查询学生详细信息(包括成绩)QueryById;
3、成绩列表函数 ListAll 按学号排序输出学生信息;
4、输出平均分 AvgScore;
5、输出最高分 MaxScore 学生信息(需要注意的是最高分的学生可能不只一人)。
利用 while 循环实现功能界面输出,根据用户选择调用对应的功能选项。所有学生成绩信息在退出系统时以文本文件保存到文件 scores.txt(格式自定,可以是每个学生的信息和成绩占一行),在系统第一次运行时 scores.txt 被创建,每次系统运行时系统将从文件读入已保存的学生成绩信息。
提交:
代码(.c 文件)、可执行文件(exe 文件)、实验报告(word 文件)和存有数据的 scores.txt。实验报告需提供成绩录入、成绩查询、成绩列表等功能的运行结果。所有文件打包成一个压缩文件。
五、实验步骤:
- 定义结构体和顺序表:
考虑用结构体来表示数据元组,每个元组应包含学号、姓名(汉语拼音)和分数三个属性。同时建一个顺序表,用其中的指针指向结构体数组的元素,可实现对数据的操作。
部分代码如下:
typedef struct grade /* 定义元组信息 */ { char StuNo[20]; /* 学号,由字符串组成 */ char Name[15]; /* 姓名,由字符串组成 */ int Score; /* 成绩,char类型 */ int Flag; }Grade; typedef struct sqlist /* 定义顺序表 */ { Grade * elem; }Sqlist;其中定义 StuNo 为学号,是 20 个字符大小的字符串,Name 为姓名,是 15 个字符大小的字符串,Score 是 int 型变量,表示成绩,最后还加了一个 int 型变量 Flag,作用是充当一个标志位,判断此处有无数据,方便程序操作。
- 除了题目中需要的函数外,还需要定义几个辅助函数,这里举几个比较典型的例子,详细代码见代码源文件。
(1)文件初始化函数:
void InitialFile() /* 文件初始化方法 */ { FILE * pf; if ( (pf = fopen( "scores.txt", "a" ) ) == NULL ) /* 打开操作不成功 */ { printf( "错误:数据文件打开失败!n" ); return; /* 结束程序的执行 */ } fclose( pf ); return; }作用:在每次程序开始时都执行一次,使用“a”方法打开文件,可以实现没有该文件自动穿件该文件,并且当该文件存在时可以不用清除其中的内容。
(2)文件读取函数:
void GetSqlist( int * mm, int * nn, Grade * b, Sqlist * a ) /* 通过现有文件创建一个顺序表 */ { FILE * pf; int tempc; int tempcc; char temps[20]; char tempss[15]; int i; int count = 0; a - > elem = b; /* 初始化 */ if ( (pf = fopen( "scores.txt", "r" ) ) == NULL ) /* 打开操作不成功 */ { printf( "错误:文件打开失败!" ); return; /* 结束程序的执行 */ } for ( i = 0; i < *nn; i++ ) { (a - > elem + i) - > Flag = 0; } for ( i = 0; i < *nn; i++ ) { tempc = fgetc( pf ); if ( tempc == EOF ) { printf( "顺序表创建完成!n" ); break; } if ( tempc == '#' ) { tempc = fgetc( pf ); if ( tempc != '#' ) { count++; fseek( pf, -1, SEEK_CUR ); fgets( temps, 20, pf ); if ( 1 ) { strcpy( (a - > elem + i) - > StuNo, temps ); fseek( pf, 3, SEEK_CUR ); fgets( tempss, 15, pf ); strcpy( (a - > elem + i) - > Name, tempss ); fseek( pf, 4, SEEK_CUR ); fscanf( pf, "%d ", &tempcc ); /* */ (a - > elem + i) - > Score = tempcc; (a - > elem + i) - > Flag = 1; } } } } *mm = count; return; }作用:在程序开始时执行此操作,可以实现将文件中的数据读取到当前内存中的效果,方便后续处理。
(3)结构体排序操作:
void SortSqlist(Sqlist * a, int * mm) //BUG { int i, j; int tempc; char temps[20]; char tempss[15]; for (i = 0; i < 20; i++) { temps[i] = ' '; } for (i = 0; i < 15; i++) { tempss[i] = ' '; } for (i = 0; i < *mm; i++) { for (j = 0; j < *mm - 1; j++) { if (strcmp((a - >elem + j) - >StuNo, (a - >elem + j + 1) - >StuNo) > 0) { strcpy(temps, (a - >elem + j) - >StuNo); strcpy((a - >elem + j) - >StuNo, (a - >elem + j + 1) - >StuNo); strcpy((a - >elem + j + 1) - >StuNo, temps); strcpy(tempss, (a - >elem + j) - >Name); strcpy((a - >elem + j) - >Name, (a - >elem + j + 1) - >Name); strcpy((a - >elem + j + 1) - >Name, tempss); tempc = (a - >elem + j) - >Score; (a - >elem + j) - >Score = (a - >elem + j + 1) - >Score; (a - >elem + j + 1) - >Score = tempc; } } } }作用:对当前数据表中元组按学号从小到大进行排序,使用的是冒泡排序法。
(4)写入文件函数
void WriteFile( Sqlist * a, int * mm ) { int i; FILE * pf; if ( (pf = fopen( "scores.txt", "w" ) ) == NULL ) /* 打开操作不成功 */ { printf( "错误:文件打开失败!" ); return; /* 结束程序的执行 */ } for ( i = 0; i < *mm; i++ ) { if ( (a - > elem + i) - > Flag == 1 ) { fprintf( pf, "#%-20s##%-15s###%-5dn", (a - > elem + i) - > StuNo, (a - > elem + i) - > Name, (a - > elem + i) - > Score ); } } fclose( pf ); return; }作用:该系统实现的逻辑是:开始时从文件中读取数据到内存,之后对数据表的操作直接在内存中进行,当数据有变动或结束程序时,重新把内存中的数据按一定格式写入文件,这个函数就是写入文件用的。
- 程序主要功能的实现:
(1)添加学生信息
void AddStu(Sqlist * a, int * mm, int * nn) { char newStuNo[20]; char newName[15]; int * newScore; int i; if ( * mm == *nn) //超出容量,返回值为0,表示出错 { printf("出错:超出数据表容量!n"); //提示:超出容量,出错 return; } GetInfo(newStuNo, newName, newScore); for (i = 0; i < *nn; i++) { if ((a - >elem + i) - >Flag == 0) { strcpy((a - >elem + i) - >StuNo, newStuNo); strcpy((a - >elem + i) - >Name, newName); (a - >elem + i) - >Score = *newScore; (a - >elem + i) - >Flag = 1; * mm = *mm + 1; break; } } printf("n信息添加成功!n"); return; }作用:从键盘输入学生信息,并将其添加到内存中的数据表。
(2)按学号查询学生信息:
void QuertById( Sqlist *a, int *mm ) /*按ID查找操作 */ { char inputStuNo[20]; int i; for ( i = 0; i < 20; i++ ) { inputStuNo[i] = ' '; } GetStuNo( inputStuNo ); for ( i = 0; i < 20; i++ ) { if ( inputStuNo[i] == '0' ) { inputStuNo[i] = ' '; } } inputStuNo[i - 1] = '0'; printf( "n" ); printf( "学号 " ); printf( "姓名 " ); printf( "成绩n" ); for ( i = 0; i < *mm; i++ ) { if ( (strcmp( inputStuNo, (a->elem + i)->StuNo ) == 0) && ( (a->elem + i)->Flag == 1) ) { printf( "%-20s", (a->elem + i)->StuNo ); printf( "%-15s", (a->elem + i)->Name ); printf( "%-5d", (a->elem + i)->Score ); printf( "n" ); } } printf( "nn查询完成!nn" ); }作用:实现按学号查询功能。从键盘中输入学号,可以在屏幕上输出相关的学生信息(包括学号、姓名和成绩)。
(3)按学号排序输出学生信息:
void ListAll(int * mm, int * nn, Sqlist * a) { SortSqlist(a, mm); int i; printf("n"); printf("学号 "); printf("姓名 "); printf("成绩n"); for (i = 0; i < *nn; i++) { if ((a - >elem + i) - >Flag == 1) { printf("%-20s", (a - >elem + i) - >StuNo); printf("%-15s", (a - >elem + i) - >Name); printf("%-5d", (a - >elem + i) - >Score); printf("n"); } } printf("n"); return; }作用:首先对结构体数组按学号排序,再遍历数组输出所有元组信息。
(4)输出平均分
void AvgScore( int *mm, int*nn, Sqlist *a ) { int i; int s = 0; float avg; for ( i = 0; i < *mm; i++ ) { s += (a->elem + i)->Score; } avg = (float) s / (*mm); printf( "平均成绩为:%.2f", avg ); printf( "n" ); return; }作用:输出当前数据表中所有元组的成绩的平均值,精确到小数点后两位。直接遍历数组,再按照平均值公式计算即可。
(5)输出最高分学生信息
void MaxScore(int * mm, int * nn, Sqlist * a) { Grade temp; int i; int Max = -100; for (i = 0; i < *mm; i++) { if (Max < (a - >elem + i) - >Score) { Max = (a - >elem + i) - >Score; } } printf("n"); printf("最高分为:%d", Max); printf("n"); printf("学号 "); printf("姓名 "); printf("成绩n"); for (i = 0; i < *mm; i++) { if ((a - >elem + i) - >Score == Max) { printf("%s", (a - >elem + i) - >StuNo); printf("%s", (a - >elem + i) - >Name); printf("%d", (a - >elem + i) - >Score); printf("n"); } } return; }作用:输出最高分学生信息,如果有多个学生都是最高分,则这些学生信息都会被输出。函数实现的逻辑是,先遍历一遍数组,找出最高分,再遍历一遍数组,只要某学生成绩为最高分,就输出这个学生的信息。
(6)除了题目要求的功能以外,本程序还实现了删除学生信息和显示当前数据表状态的功能。
void DelById(Sqlist * a, int * mm) { char inputStuNo[20]; int i; for (i = 0; i < 20; i++) { inputStuNo[i] = '0'; } GetStuNo(inputStuNo); //BUG for (i = 0; i < 20; i++) { if (inputStuNo[i] == '0') { inputStuNo[i] = ' '; } } inputStuNo[i - 1] = '0'; for (i = 0; i < *mm; i++) { if (strcmp(inputStuNo, (a - >elem + i) - >StuNo) == 0) { (a - >elem + i) - >Flag = 0; } } * mm = *mm - 1; printf("n删除成功!n"); return; }作用:按学号删除函数,原理是通过修改元组的 Flag 标志位来实现删除。并且下一次再加入新元组时,该位置会被替代,从而实现了不浪费空间的效果。
同时,在主函数中定义了两个 int 型变量 x,y,一个用于表示数据表的最大容量,一个用于表示当前容量,这两个变量会在函数操作中不断变化,从而实现可以实时查看数据表状态和防止数据溢出的效果。
- 主函数循环体的实现
for (;;) { 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("按0:退出程序n"); printf("n"); scanf("%d", &a); switch (a) { case 0: { WriteFile(aa, mm); printf("感谢您的使用!n"); return 0; } case 1: { AddStu(aa, mm, nn); getchar(); WriteFile(aa, mm); break; } case 2: { QuertById(aa, mm); break; } case 3: { ListAll(mm, nn, aa); WriteFile(aa, mm); break; } case 4: { AvgScore(mm, nn, aa); break; } case 5: { MaxScore(mm, nn, aa); break; } case 6: { DelById(aa, mm); WriteFile(aa, mm); break; } case 7: { printf("当前数据容量:%dn", *mm); printf("最大数据容量:%dn", *nn); break; } case 8: { WriteFile(aa, mm); printf("保存成功!n"); break; } default: { printf("非法命令!n"); printf("n"); break; } } }作用:实现程序界面的循环。通过一个无条件的 for 循环和 switch 语句来实现各个功能。
六、实验结果分析:
测试平台:
Windows 10 64 位
C-Free 5.0
- 程序主页面如下:
图 1.程序主界面图
- 添加学生信息:
图 2.添加学生信息界面图
通过相同的方法添加其他几个学生信息,最后写入文件的效果为:
图 3.文件信息图
数据在文件中按图 3 所示的格式进行存储,方便读写的同时方法阅读。
- 按学号查找信息
图 4.查询界面图
- 按学号排序查看所有成绩信息
图 5.查看所有成绩界面图
- 查看成绩平均分
图 5.查看成绩平均分界面图
- 查看最高分学生信息
图 6.查看最高分学生信息界面图
发现可以显示多个成绩相同的学生的信息。
- 按学号删除成绩信息
图 7.删除学生成绩信息界面图
删之后查看所有学生信息,发现正确:
图 8.删除信息后查看所有学生信息界面图
- 查看当前数据表状态
图 9.查看当前数据表状态界面图
- 保存当前数据到文件
图 10.文件保存成功提示图
- 退出程序
图 11.程序退出界面示意图
七、实验结论:
1、C 语言可以实现简单成绩管理系统;
2、模块化程序设计需要先规划好程序的各个模块功能;
3、调试是程序设计中必不可少的一环。
八、总结及心得体会:
1、要善于使用调试功能,这会让程序设计事半功倍;
2、多掌握编程语言的语法和库函数,可以让程序实现更多功能;
3、在编程前一定要先在纸上记录大纲。
九、对本实验的改进建议:
1、可以多设置几个难度差不多的题目供同学们选择。
♻️ 资源
大小:76.2KB
➡️资源下载:https://download.csdn.net/download/s1t16/87404243
注:更多内容可关注微信公众号【神仙别闹】,如当前文章或代码侵犯了您的权益,请私信作者删除!