C语言实现简易学生成绩管理系统:结构体、菜单与增删改查
在C语言学习过程中,学生成绩管理系统是一个涵盖结构体、函数、指针及基本数据结构的经典练手项目。本文通过拆解功能模块,详细演示如何利用静态数组(顺序表)配合菜单交互,完成对学生信息的录入、查询、删除、修改、插入、排序及统计。
系统功能需求与界面设计
一个基础的学生管理系统至少应包含以下功能:成绩录入、成绩查询、成绩删除、成绩修改、成绩插入、成绩排序以及学生人数统计。为了实现良好的交互体验,我们需要设计一个控制台循环菜单,让用户通过数字选项调用对应功能,并在每次操作后保持程序运行状态直至用户选择退出。
系统主界面的效果展示如下:
菜单的核心逻辑是通过 while(1) 无限循环配合 switch-case 语句来实现。每次循环时,打印菜单选项,读取用户输入的 choice,根据输入值调用相应的处理函数。使用 system("cls") 清屏和 system("pause") 暂停可以使界面更清晰,便于观察结果。
void Menu()
{
while(1)
{
printf("\t\t\t---------------STUDENT---------------\t\t\t\n");
printf("\t\t\t0.退出\t\t\t\n");
printf("\t\t\t1.录入成绩\t\t\t\n");
printf("\t\t\t2.查询成绩\t\t\t\n");
printf("\t\t\t3.删除成绩\t\t\t\n");
printf("\t\t\t4.修改成绩\t\t\t\n");
printf("\t\t\t5.插入成绩\t\t\t\n");
printf("\t\t\t6.排序\t\t\t\n");
printf("\t\t\t7.统计学生人数\t\t\t\n");
printf("\t\t\t-------------------------------------\t\t\t\n");
printf("\t\t\t选择(0---7)\t\t\t");
int choice;
scanf("%d", &choice);
switch(choice)
{
case 0: exit(0); break;
case 1: in(); save(); system("pause"); system("cls"); break;
case 2: show(); system("pause"); system("cls"); break;
case 3: del(); save(); system("pause"); system("cls"); break;
case 4: modify(); system("pause"); system("cls"); break;
case 5: insert(); save(); system("pause"); system("cls"); break;
case 6: order(); system("pause"); system("cls"); break;
case 7: total(); system("pause"); system("cls"); break;
default: printf("\n输入错误,请输入0---7\n\n"); system("pause"); system("cls"); break;
}
}
}数据结构定义:结构体与全局变量
由于这是一个简易系统,我们采用定长静态数组来模拟数据库的存储。每个学生对象需要包含学号、姓名以及各科成绩信息。我们将这些信息封装在一个 struct 结构体中,并定义一个足够大的数组 student[N] 来存储多个学生实例。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N 100 // 最大学生数量
// 定义学生结构体
typedef struct student
{
char num[20]; // 学号
char name[20]; // 姓名
float elec; // 选修课成绩
float expe; // 实验课成绩
float requ; // 必修课成绩
float sum; // 总成绩
float ave; // 平均成绩
} Student;
Student student[N]; // 定义全局结构体数组
int count = 0; // 当前实际学生人数
第1步:成绩的录入 (in)
目的:从控制台获取学生信息,自动计算总分和平均分,并存入数组。
操作逻辑:
- 询问用户要录入多少个学生。
- 遍历指定次数,依次提示输入学号、姓名及三门成绩。
- 在输入完成后,立即计算
sum和ave。 - 更新全局计数器
count。
void in()
{
int i, n;
printf("输入录取学生的数量:\n");
scanf("%d", &n);
for (i = 1; i <= n; i++)
{
printf("请输入第%d位学生学号:\n", i);
scanf("%s", student[i].num);
printf("请输入第%d位学生姓名:\n", i);
scanf("%s", student[i].name);
printf("请输入第%d位学生选修课成绩:\n", i);
scanf("%f", &student[i].elec);
printf("请输入第%d位学生实验课成绩:\n", i);
scanf("%f", &student[i].expe);
printf("请输入第%d位学生必修课成绩:\n", i);
scanf("%f", &student[i].requ);
// 自动计算总分和平均分
student[i].sum = student[i].elec + student[i].expe + student[i].requ;
student[i].ave = student[i].sum / 3;
count++; // 增加实际人数
}
printf("录入成功\n");
}第2步:成绩的查询 (show)
目的:根据输入的学号,在数组中查找并显示对应的学生详细信息。
操作逻辑:
- 接收用户输入的待查询学号字符串。
- 遍历数组,利用
strcmp()对比当前学生的num字段。 - 如果匹配,则格式化输出该学生的所有成绩信息。
void show()
{
int i;
char xh[20];
printf("请输入要查询的学生学号:\n");
getchar(); // 消耗上一次输入残留的回车符
gets(xh);
for (i = 0; i < N; i++)
{
if (student[i].num[0] != '\0' && strcmp(student[i].num, xh) == 0)
{
printf("\n学号:%-5s 姓名:%-5s 选修课成绩:%-5.1f 实验课成绩:%-5.1f 必修课成绩:%-5.1f 总分:%-5.1f 平均分:%-5.1f\n",
student[i].num, student[i].name, student[i].elec,
student[i].expe, student[i].requ, student[i].sum, student[i].ave);
return; // 找到后可直接退出函数,避免重复打印
}
}
printf("未找到该学号的学生\n");
}第3步:成绩的删除 (del)
目的:定位目标学生并从数组中移除,后续学生前移填补空缺。
操作逻辑:
- 输入要删除的学号。
- 查找该学号在数组中的索引
a。 - 从该位置开始,将后一个元素赋值给当前元素,直到最后一个学生被移到倒数第二位。
- 全局计数
count减 1。
void del()
{
int j, a = 0, k = count;
char xh[25];
printf("所要删除的学生学号为:\n");
scanf("%s", xh);
flush(stdin); // 清理缓冲区
for (j = 0; j < count; j++)
{
if (strcmp(student[j].num, xh) == 0)
{
// 找到后,将该位置之后的所有元素向前挪动一位
for (a = j; a < count - 1; a++)
{
student[a] = student[a + 1];
}
count--; // 人数减一
printf("删除成功\n");
return; // 删除后无需继续查找
}
}
if (j == count) // 修正原逻辑判断,防止越界或错误提示
{
printf("没有你所要删除的学生的信息\n");
}
}第4步:成绩的修改 (modify)
目的:先锁定特定学生,再允许修改单科成绩,最后更新总分与均分。
操作逻辑:
- 输入学号找到目标索引
j。 - 提供子菜单,让选择修改哪一门课程的成绩。
- 输入新成绩数值,存入对应字段。
- 重新计算该生的
sum和ave。 - 显示修改后的完整记录以供确认。
void modify()
{
int i, j, h;
float score;
char xh[20];
printf("请输入修改的学生的学号:\n");
scanf("%s", xh);
// 查找目标学生
j = -1;
for(i = 0; i < N; i++)
{
if (strcmp(student[i].num, xh) == 0)
{
j = i;
break;
}
}
if (j == -1) {
printf("找不到该学生\n");
return;
}
printf("请选择修改内容\n");
printf("1.修改该学生的选修课成绩\n");
printf("2.修改该学生的实验课成绩\n");
printf("3.修改该学生的必修课成绩\n");
scanf("%d", &h);
switch (h)
{
case 1:
printf("请输入该学生的选修课成绩\n");
scanf("%f", &score);
student[j].elec = score;
break;
case 2:
printf("请输入该学生的实验课成绩\n");
scanf("%f", &score);
student[j].expe = score;
break;
case 3:
printf("请输入该学生的必修课成绩\n");
scanf("%f", &score);
student[j].requ = score;
break;
default:
printf("\n输入错误,请输入1---3\n\n");
return;
}
// 重新计算总分和平均分
student[j].sum = student[j].elec + student[j].expe + student[j].requ;
student[j].ave = student[j].sum / 3;
printf("修改后为:\n");
printf("\n学号:%-5s 姓名:%-5s 选修课成绩:%-5.1f 实验课成绩:%-5.1f 必修课成绩:%-5.1f 总分:%-5.1f 平均分:%-5.1f\n",
student[j].num, student[j].name, student[j].elec,
student[j].expe, student[j].requ, student[j].sum, student[j].ave);
printf("\n修改成功\n");
}第5步:成绩的插入 (insert)
目的:在数组末尾添加新学生,并处理潜在的学号重复问题。
操作逻辑:
- 输入要插入的人数
m。 - 在填入每个新学生信息前,检查其学号是否与现有学号冲突。
- 若不冲突,则正常录入;若冲突,要求重新输入。
- 录入完成后,更新
count。
void insert()
{
int m, flag, i, j, k;
printf("请输入待添加的学生人数:\n");
scanf("%d", &m);
if (m > 0)
{
k = count + 1;
for(i = k; i <= count + m; i++)
{
flag = 1;
while (flag != 0)
{
flag = 0;
printf("请输入第%d位学生的学号:\n", i);
getchar();
gets(student[i].num);
// 检查学号唯一性
for (j = 0; j < count; j++)
{
if (strcmp(student[i].num, student[j].num) == 0)
{
printf("该学号已存在,请重新输入!\n");
flag = 1;
break;
}
}
}
printf("请输入第%d位学生姓名:\n", i);
gets(student[i].name);
printf("请输入第%d位学生选修课成绩:\n", i);
scanf("%f", &student[i].elec);
printf("请输入第%d位学生实验课成绩:\n", i);
scanf("%f", &student[i].expe);
printf("请输入第%d位学生必修课成绩:\n", i);
scanf("%f", &student[i].requ);
student[i].sum = student[i].elec + student[i].expe + student[i].requ;
student[i].ave = student[i].sum / 3;
}
count += m;
printf("添加完成!\n");
}
}第6步:成绩的排序 (order)
目的:按照学生的总分从高到低进行排列,方便排名查看。
操作逻辑:
这里使用简单的交换排序(Selection Sort 变体)。外层循环控制轮次,内层循环寻找剩余部分中的最低分(或最高分),通过临时结构体变量交换位置。
void order()
{
int i, j, m;
// 冒泡/选择排序:按总分升序
for(j = 1; j <= count - 1; j++)
{
m = j;
for(i = j + 1; i <= count; i++)
{
// 如果需要降序,改为 > 符号
if(student[i].sum < student[m].sum)
{
Student temp = student[i];
student[i] = student[m];
student[m] = temp;
}
}
}
printf("排序后的成绩列表:\n");
for(j = 1; j <= count; j++)
{
printf("\n学号:%-5s 姓名:%-5s 选修课成绩:%-5.1f 实验课成绩:%-5.1f 必修课成绩:%-5.1f 总分:%-5.1f 平均分:%-5.1f\n",
student[j].num, student[j].name, student[j].elec,
student[j].expe, student[j].requ, student[j].sum, student[j].ave);
}
}第7步:统计学生人数 (total)
目的:显示当前系统中存在的实时学生数量。
void total()
{
printf("当前学生总人数: %d\n", count);
}文件保存功能 (save)
为了防止数据在程序关闭后丢失,我们需要实现一个简单的文件保存功能。使用标准库中的文件操作函数 fopen, fwrite, fclose 将结构体数组的内容写入磁盘二进制文件。
void save()
{
FILE* fp;
char filename[100];
int i;
printf("请输入要保存的文件名(例如 students.dat):\n");
scanf("%s", filename);
if ((fp = fopen(filename, "wb")) == NULL) // 使用 wb 模式以二进制写入
{
printf("打开文件失败!\n");
return;
}
// 写入从第1个到第count个学生
for (i = 1; i <= count; i++)
{
if (fwrite(&student[i], sizeof(Student), 1, fp) != 1)
{
printf("保存失败!\n");
fclose(fp);
return;
}
}
printf("数据保存成功至: %s\n", filename);
fclose(fp);
}总结
本案例完整地展示了C语言中面向过程编程的典型结构:通过全局数组维护状态,模块化编写各个功能函数。在实际开发中,建议在此基础上增加“从文件加载数据 (load)”的功能,以实现数据的持久化保留。以上就是C语言实现简易学生管理系统的详细内容,更多关于C语言结构体应用和基础算法的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









