学生成绩管理系统——C
一、环境
编译器:Dev C
语言:C
说明:现在支持纯 C 的比较少,都是 C/C++ 都用 gcc 编译(能编译 C++ 的能编译 C,但能编译C的不一定能编译 C++),编译器问题不大,不能用 VS2017 后的版本编译,因为 VS 引入了很多安全函数限制,虽然也好但是麻烦。
二、功能概述
做综合程序设计时,都会用到多模块编程技术
- 文件级模块
- 函数级模块
这个综合设计不大,就仅是函数级模块运用。
如上图所示:,学生成绩管理系统由 4 个模块组成。
(1)输入/输出模块。学生成绩可以从键盘上直接录入;录入的信息暂时保存在内存中,并可以随时在屏幕上显示。
(2)查询模块。当录入比较多的数据记录以后,要求可以指定的记录。
(3)更新模块。主要对学生记录进行维护,主要实现记录的删除、修改、排序功能。
(4)文件管理模块。主要负责数据文件的管理,包括新建、读取、保存数据文件。
三、设计思路
3.1、数据结构
利用结构体存储学生的信息
- 优点:简单、高效,对单个学生记录的存取、修改记录很方便,因为这些操作在一整块内存中进行,内存的分配和释放只做一次。
- 缺点:需要预先估计学生的数量来分配内存大小。
typedef struct student {
int num; //学号
char name[20]; //姓名
int cscore; //语文成绩
int mscore; //数学成绩
int escore; //英语成绩
int sum; //总成绩
int place; //排名
}STU;
此外还需要另一个结构体来指向学生数据单元,记录个数,为文件操作做准备。
typedef struct pointer_info {
STU* pHead; //指向第一个数据单元的指针
int count; //数据单元的个数
char fname[20]; //数据记录的文件名
int saveflag; //1:未保存,0:已保存
}PI;
3.2、主函数 mian()
主函数是整个程序的入口,主函数只是作为各模块的入口,其功能由个子函数来实现。
主要就是显示主界面,然后让其子函数实现这些功能。
3.3、输入/输出模块
输入/输出模块由主界面选项 1 和 2 来实现。输入模块负责从键盘输入学生的记录,该函数将数据存入数据单元,指针后移,个数加 1。输出模块只需遍历指针访问每个数据单元就可以得到全部信息并打印。
子函数:Add(PI* pi)、Display(PI* pi)
3.4、查询模块
查询模块由主界面选项 5 实现。提供按学号、姓名查询,只需要遍历指针访问每个数据单元,然后看是否匹配,匹配成功即找到,则将找到的数据打印到屏幕。
子函数:Query(PI* pi)
3.5、更新模块
更新模块提供三种基本功能,即删除、修改、排序。
删除:找到要删除的数据,利用指针将后面的数据前移覆盖即可。
修改:找到对应数据单元,进行相应的修改。
排序:冒泡法排序
子函数:Delete(PI* pi)、Modify(PI* pi)、Sort(PI* pi)
3.6、文件管理模块
文件管理主要包含对数据记录文件的建立、读取、保存等基本操作。以二进制格式读写,因为 C 比较简单,内存可以直接读取,转换格式比较麻烦。
子函数:New(PI* pi)、Load(PI* pi)、Save(PI* pi)
四、源代码
由一些宏定义,主要是为了后面能够减写,就把常用的和比较长的红花定义了一下。
源码里面有注释,应该能够全部看懂。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
(1)定义各种格式
(2)定义 DATA 为了后面省写
*/
#define N 1024
#define FORMAT1 "---------------------------------------------------------------\n"
#define FORMAT2 "| Num | Name | Chinese | Math | English | Sum | Place |\n"
#define FORMAT3 "|-------|--------|---------|--------|---------|-------|-------|\n"
#define FORMAT4 "|%7d|%8s|%9d|%8d|%9d|%7d|%7d|\n"
#define DATA p->num,p->name,p->cscore,p->mscore,p->escore,p->sum,p->place
typedef struct student {
int num; //学号
char name[20]; //姓名
int cscore; //语文成绩
int mscore; //数学成绩
int escore; //英语成绩
int sum; //总成绩
int place; //排名
}STU;
typedef struct pointer_info {
STU* pHead; //指向第一个数据单元的指针
int count; //数据单元的个数
char fname[20]; //数据记录的文件名
int saveflag; //1:未保存,0:已保存
}PI;
void Menu(); //主界面
void Add(PI* pi); //添加数据记录
void Display(PI* pi); //显示数据记录
void Delete(PI* pi); //删除数据记录
void Save(PI* pi); //保存数据记录
void Query(PI* pi); //查询数据激励
void Modify(PI* pi); //修改数据记录
void Sort(PI* pi); //排序数据记录
void Load(PI* pi); //加载数据记录文件
void New(PI* pi); //新建数据记录文件
void Wrong(); //错误提示
void printheader(); //格式化输出表头
int main() {
int sel;
STU* pstu;
PI pi;
pstu = (STU*)malloc(sizeof(STU) * N); //分配数据单元内存
pi.pHead = pstu; //初始化 pi 的成员
pi.count = 0;
pi.fname[19] = '\0';
pi.saveflag = 0;
Menu(); //显示主界面
do {
printf("请输入您的选择[0-9]:");
scanf("%d", &sel); //获取键盘输入
if (sel == 0) { //输入 0,退出
if (pi.saveflag == 1) { //如果数据被修改但没有保存,则先保存
if (strlen(pi.fname)) {
Save(&pi);
}
else {
New(&pi);
Save(&pi);
}
}
break;
}
switch (sel) //根据键盘输入进入响应模块
{
case 1: Add(&pi); break; //添加学生记录
case 2: Display(&pi); break; //显示学生记录
case 3: Delete(&pi); break; //删除学生记录
case 4: Modify(&pi); break; //修改学生记录
case 5: Query(&pi); break; //查询学生记录
case 6: Sort(&pi); break; //排序学生记录
case 7: Load(&pi); break; //加载数据文件
case 8: New(&pi); break; //新建数据文件
case 9: Save(&pi); break; //保存数据文件
default: Wrong(); break; //按键有误
}
} while (1);
pstu = pi.pHead;
free(pstu); //释放内存
return 0;
}
void printheader() { //格式化输出表头
printf(FORMAT1);
printf(FORMAT2);
}
void Menu() { //主界面
system("cls");
printf("\t\t\t\t**********************************************\n");
printf("\t\t\t\t* 学生成绩管理系统 *\n");
printf("\t\t\t\t**********************************************\n");
printf("\t\t\t\t* 1、添加学生信息 *\n");
printf("\t\t\t\t* 2、显示学生信息 *\n");
printf("\t\t\t\t* 3、删除学生信息 *\n");
printf("\t\t\t\t* 4、修改学生信息 *\n");
printf("\t\t\t\t* 5、查询学生信息 *\n");
printf("\t\t\t\t* 6、排序学生信息 *\n");
printf("\t\t\t\t* 7、打开文件 *\n");
printf("\t\t\t\t* 8、新建文件 *\n");
printf("\t\t\t\t* 9、保存文件 *\n");
printf("\t\t\t\t* 0、退出 *\n");
printf("\t\t\t\t**********************************************\n");
}
void Wrong() { //按键错误信息
printf("输入有误!\n");
}
void Add(PI* pi) { //由键盘录入学生成绩记录
int num;
int numRepeatFlag = 0;
STU* pstu, * p;
do {
pstu = pi->pHead + pi->count; //pstu 指向下一个需要写入数据的单元
p = pi->pHead; //p 用来寻找重复的学号,因此指向数据的第一个单元
printf("请输入学号(输入 0 返回主界面):");
scanf("%d", &num);
for (int i = 1; i <= pi->count; i++) { //学号重复处理,学号不能重复
if (num == p->num) {
printf("该学号已存在,请重新输入其它学号!\n");
numRepeatFlag = 1;
break;
}
p++;
}
if (numRepeatFlag) {
numRepeatFlag = 0;
continue;
}
if (num != 0) { //如果输入不为 0,则继续输入学生数据,否则退回主界面
pstu->num = num;
}
else {
Menu();
break;
}
printf("请输入姓名:");
scanf("%s", pstu->name);
printf("请输入语文成绩:");
scanf("%d", &pstu->cscore);
printf("请输入数学成绩:");
scanf("%d", &pstu->mscore);
printf("请输入数学成绩:");
scanf("%d", &pstu->escore);
pstu->sum = pstu->cscore + pstu->mscore + pstu->escore; //计算总分
pstu->place = 0; //排名暂时置 0
pi->count++; //数据单元自增 1
pi->saveflag = 1; //数据改变,为保存标志置 1
} while (1);
}
void Display(PI* pi) { //在屏幕上显示学生数据记录
STU* p;
Menu();
p = pi->pHead;
if (pi->count == 0) {
printf("没有学生信息!\n");
return;
}
printheader(); //打印表格头部
for (int i = 1; i <= pi->count; i++) { //打印数据序列
printf(FORMAT3);
printf(FORMAT4, DATA);
p++;
}
printf(FORMAT1); //打印表格结尾
}
void Query(PI* pi) {
int sel;
int num;
int i;
STU* p;
char name[20];
p = pi->pHead; //p 指向数据序列首部的指针
Menu();
printf("根据学号查询——1\n"); //1:根据学号查询,2:根据姓名查询,其它:返回
printf("根据姓名查询——2\n");
printf("返回——————其它\n");
printf("请输入你的选择:");
scanf("%d", &sel);
if (sel == 1) { //按学号查询
printf("请输入你要查询的学号:");
scanf("%d", &num);
for (i = 0; i <= pi->count; i++) { //学号搜索
if (num == p->num) {
break;
}
p++;
}
if (i > pi->count) {
printf("找不到这个学生学号!\n");
return;
}
else {
printheader(); //找到则打印输出
printf(FORMAT4, DATA);
printf(FORMAT1);
}
}
else if (sel == 2) { //按姓名查询
printf("请输入你要查询的姓名:");
scanf("%s", name);
for (i = 1; i <= pi->count; i++) { //按姓名搜索
if (!strcmp(name, p->name)) {
break;
}
p++;
}
if (i > pi->count) {
printf("找不到这个学生姓名!\n");
return;
}
else { //找到则打印输出
printheader();
printf(FORMAT4, DATA);
printf(FORMAT1);
}
}
else {
return;
}
}
void Modify(PI* pi) {
int sel;
int num;
char name[20];
int i;
STU* p;
p = pi->pHead;
Menu();
Display(pi);
printf("1————根据学号修改\n"); //1:根据学号修改,2:根据姓名修改,其它:返回
printf("2————根据姓名修改\n");
printf("其它———返回\n");
printf("请输入你的选择:");
scanf("%d", &sel);
if (sel == 1) {
printf("请输入要修改的学生的学号:");
scanf("%d", &num);
for (i = 1; i <= pi->count; i++) {
if (num == p->num) {
break;
}
p++;
}
if (i > pi->count) {
printf("找不到这个学生学号!\n");
return;
}
else {
printf("请输入姓名:");
scanf("%s", p->name);
printf("请输入语文成绩:");
scanf("%d", &p->cscore);
printf("请输入数学成绩:");
scanf("%d", &p->mscore);
printf("请输入数学成绩:");
scanf("%d", &p->escore);
p->sum = p->cscore + p->mscore + p->escore;
p->place = 0;
pi->saveflag = 1;
}
}
else if (sel == 2) {
printf("请输入要修改的学生的姓名:");
scanf("%s", name);
for (i = 1; i <= pi->count; i++) {
if (!strcmp(name, p->name)) {
break;
}
p++;
}
if (i > pi->count) {
printf("找不到这个学生姓名!\n");
return;
}
else {
printf("请输入姓名:");
scanf("%s", p->name);
printf("请输入语文成绩:");
scanf("%d", &p->cscore);
printf("请输入数学成绩:");
scanf("%d", &p->mscore);
printf("请输入数学成绩:");
scanf("%d", &p->escore);
p->sum = p->cscore + p->mscore + p->escore;
p->place = 0;
pi->saveflag = 1;
}
}
else {
return;
}
}
void Delete(PI* pi) {
int sel;
int num;
char name[20];
int i;
STU* p;
p = pi->pHead;
Menu();
printf("1————根据学号删除\n"); //1:根据学号删除,2:根据姓名删除,其它:返回
printf("2————根据姓名删除\n");
printf("其它———返回\n");
printf("请输入你的选择:");
scanf("%d", &sel);
if (sel == 1) {
printf("请输入你要删除的学号:");
scanf("%d", &num);
for (i = 1; i <= pi->count; i++) {
if (num == p->num) {
break;
}
p++;
}
if (i > pi->count) {
printf("找不到这个学生学号!\n");
return;
}
else if (i == pi->count) {
pi->count--;
pi->saveflag = 1;
printf("删除成功!\n");
}
else {
memcpy(p, p + 1, (pi->pHead + pi->count - p) * sizeof(STU));
pi->count--;
pi->saveflag = 1;
printf("删除成功!\n");
}
}
else if (sel == 2) {
printf("请输入你要删除的姓名:");
scanf("%s", name);
for (i = 1; i <= pi->count; i++) {
if (!strcmp(name, p->name)) {
break;
}
p++;
}
if (i > pi->count) {
printf("找不到这个学生姓名!\n");
return;
}
else if (i == pi->count) {
pi->count--;
pi->saveflag = 1;
printf("删除成功!\n");
}
else {
memcpy(p, p + 1, (pi->pHead + pi->count - p) * sizeof(STU));
pi->count--;
pi->saveflag = 1;
printf("删除成功!\n");
}
}
else {
return;
}
}
void Swap(STU* p1, STU* p2) { //交换两个数据单元
STU stmp;
stmp = *p1;
*p1 = *p2;
*p2 = stmp;
}
void Sort(PI* pi) {
int i, j;
int sel;
STU* p;
printf("1————根据学号排序\n"); //1:根据学号排序,2:根据成绩排序,其它:返回
printf("2————根据成绩排序\n");
printf("其它———返回\n");
printf("请输入你的选择:");
scanf("%d", &sel);
if (sel == 1) { //冒泡法,按学号排序
for (i = 1; i <= pi->count; i++) {
p = pi->pHead;
for (j = 1; j <= pi->count - 1; j++, p++) {
if (p->num > (p + 1)->num) {
Swap(p, p + 1);
}
}
}
pi->saveflag = 1;
}
else if (sel == 2) { //冒泡法,按成绩排序
for (i = 1; i <= pi->count; i++) {
p = pi->pHead;
for (j = 1; j <= pi->count - 1; j++, p++) {
if (p->sum < (p + 1)->sum) {
Swap(p, p + 1);
}
}
}
p = pi->pHead;
for (i = 1; i <= pi->count; i++) { //排序后的顺序就是排名
p->place = i;
p++;
}
pi->saveflag = 1;
}
else {
return;
}
Display(pi); //排序后,打印输出
}
void New(PI* pi) { //新建数据记录文件
FILE* fp;
char name[20];
char fname[20] = ".\\"; //当前文件夹
printf("请输入创建文件名字:");
scanf("%s", name);
strcat(fname, name); //文件名称
strcat(fname, ".dat"); //文件后缀 ".dat"
fp = fopen(fname, "wb"); //以只写方式打开二进制文件
if (fp == NULL) {
printf("新建文件失败!\n");
return;
}
strcpy(pi->fname, fname);
printf("新建数据文件 %s 成功!\n", name);
fclose(fp);
}
void Load(PI* pi) { //加载数据记录文件
FILE* fp;
STU* p = pi->pHead;
char name[20];
char fname[20] = ".\\";
printf("请输入打开文件名字:");
scanf("%s", name);
strcat(fname, name);
fp = fopen(fname, "rb"); //以只读方式打开二进制文件
if (fp == NULL) {
printf("新建文件失败!\n");
return;
}
pi->count = 0;
pi->saveflag = 0;
while (!feof(fp)) {
if (fread(p, sizeof(STU), 1, fp)) {
pi->count++;
p++;
}
}
strcpy(pi->fname, name);
printf("加载数据文件 %s 成功!\n", name);
fclose(fp);
}
void Save(PI* pi) {
FILE* fp;
int numwriten;
if (!strlen(pi->fname)) { //判断数据记录文件是否存在,如果不存在则新建一个
New(pi);
}
fp = fopen(pi->fname, "wb");
if (fp == NULL) {
printf("打开文件失败!\n");
fclose(fp);
return;
}
if (pi->count) { //如果数据不为空,则写入文件
numwriten = fwrite(pi->pHead, 1, pi->count * sizeof(STU), fp);
pi->saveflag = 0;
printf("保存文件成功!\n");
}
else {
printf("保存文件失败!\n");
}
fclose(fp);
}
五、结果
自己试验下应该就懂了。
添加显示数据
排序成绩
说明:
(1)文件在当前文件夹,新建只需要输入文件名就可以了,格式默认为 dat,但是打开文件需要输入全部,例如:
新建文件:student
打开文件:student.dat
六、总结
1、模块化