概述
学完了C语言基础部分,根据现有知识点写了这个小项目。功能要求实现学生成绩的录入、输出、排序、平均分计算以及按学号查找成绩,另外,还要有基本的帮助菜单、清屏、退出功能。
思路
创建学生结构体,结构体成员包括学号id、语文、数学、英语三科成绩和平均分,再将各个功能通过函数进行封装,通过主函数的循环调用以达到要求的功能。另外,最近正好学了LinuxC高级,可以采用分文件编程,将封装的函数放在student.c中,函数声明放在student.h中,主函数放在main.c里面,最后,再用makefile进行工程管理(这里用个人感觉杀鸡用牛刀,以后工程文件多了应该用得多)。
结构体的构建
typedef struct
{
int id;
float chinese;
float math;
float english;
float average;
} Student;
这里用typedef对结构体重命名,方便以后调用。结构体定义应该放在student.h文件中,如果和放到student.c中的话会导致主函数找不到这个结构体,因为我们在main.c中只对.h类型的头文件进行引用,而不会对.c文件进行引用。
函数
主函数
#include <stdio.h>
#include student.h
#include <stdlib.h>
int main()
{
Student stu[99];
int stunum = 0;
char mod;
menu();
while (1)
{
printf(请输入命令=);
scanf( %c, &mod);
if (mod == 'T')
{
if (stunum == 0)
Type(stu, &stunum);
else
printf(成绩已录入,请勿重复录入!);
}
else if (mod == 'A')
{
if (stunum == 0)
printf(成绩表为空!请先使用命令T录入学生成绩。\n);
else
Average(stu, &stunum);
}
else if (mod == 'L')
{
if (stunum == 0)
printf(成绩表为空!请先使用命令T录入学生成绩。\n);
else
List(stu, &stunum);
}
else if (mod == 'P')
{
if (stunum == 0)
printf(成绩表为空!请先使用命令T录入学生成绩。\n);
else
Sort(stu, &stunum);
}
else if (mod == 'S')
{
if (stunum == 0)
printf(成绩表为空!请先使用命令T录入学生成绩。\n);
else
Search(stu, &stunum);
}
else if (mod == 'H')
menu();
else if (mod == 'C')
system(clear);
else if (mod == 'Q')
break;
else
printf(命令有误,请重新输入!\n);
}
return 0;
}
主函数主要是对结构体数组、学生人数和输入命令的定义。命令的判断则分为两类,第一类是需要判断成绩是否录入,如:录入、输出、排序、平均分计算以及按学号查找,第二类则是无需判断成绩是否录入,如:菜单、清屏和退出。如果没有录入成绩,输入命令T则调用Type函数录入成绩,输入命令A、L、T、S则输出“成绩表为空!请先使用命令T录入学生成绩。“;如果已经录入了成绩,输入命令T输出成绩已录入,请勿重复录入!,输入命令A、L、T、S则调用响应的函数实现对应功能;输入命令H、C、Q无需判断,直接执行响应功能语句。
帮助菜单
void menu()
{
for (int i = 0; i < 46; i++)
printf(*);
printf(\n);
printf( * 学生成绩管理系统——帮助菜单 * \n);
for (int i = 0; i < 46; i++)
printf(*);
printf(\n);
printf( * H = 显示帮助菜单 * \n);
printf( * T = 成绩录入 * \n);
printf( * A = 计算学生平均分 * \n);
printf( * L = 列出成绩表 * \n);
printf( * P = 按平均由高到低排序 * \n);
printf( * S = 按学号查询学生成绩 * \n);
printf( * C = 清屏 * \n);
printf( * Q = 退出系统 * \n);
for (int i = 0; i < 46; i++)
printf(*);
printf(\n);
printf( * Copyright <C> 2024.07.10 By name * \n);
for (int i = 0; i < 46; i++)
printf(*);
printf(\n);
}
帮助菜单写得比较粗糙,对菜单上下的分割线用for循环,中间的选项只能用printf挨个打印,实在也想不出什么比较简单优雅的方法,在终端运行出来就是下面这样:
成绩录入
void Type(Student *stu, int *stunum)
{
printf(请输入学生人数:);
scanf(%d, stunum);
printf(请输入%d名学生的三门课成绩:\n, *stunum);
printf(学号 语文 数学 外语\n);
for (int i = 0; i < *stunum; i++)
{
printf(%d , i + 1);
stu[i].id = i + 1;
stu[i].average = 0;
scanf(%f %f %f, &stu[i].chinese, &stu[i].math, &stu[i].english);
}
}
要保证函数的正常运行,首先必须要在主函数定义一个Student类型的结构体数组,然后在该函数中循环遍历赋值,循环次数取决于输入的学生人数stunum,学号则是自动生成,第一个录入成绩则是1号,需要注意的是这里的i代表的是结构体数组的下标,所以学号的话还要加1,函数的第二个参数是需要传入stunum的地址,这样才能在函数内部对值进行修改,因为后需要对stunum的值进行判断,判断是否录入了成绩。
成绩输出
void List(Student *stu, int *stunum)
{
printf(学生成绩如下:\n);
printf(学号 语文 数学 外语 平均分\n);
for (int i = 0; i < *stunum; i++)
{
printf(%d %.1f %.1f %.1f %.1f\n, stu[i].id, stu[i].chinese, stu[i].math, stu[i].english, stu[i].average);
}
}
这里对每个结构体数组每个元素的每个成员进行遍历打印,以达到输出成绩列表的目的。
计算平均分
void Average(Student *stu, int *stunum)
{
for (int i = 0; i < *stunum; i++)
{
stu[i].average = (stu[i].chinese + stu[i].math + stu[i].english) / 3;
}
printf(平均分已计算,请使用命令L查看。\n);
}
将结构体数组每个元素的语文成员、数学成员、英语成员相加除以3,再赋值给平均数成员即可。
排序
void Sort(Student *stu, int *stunum)
{
for (int i = 0; i < *stunum - 1; i++)
{
for (int j = 0; j < *stunum - 1 - i; j++)
{
if (stu[j].average < stu[j + 1].average)
{
Student t;
t = stu[j];
stu[j] = stu[j + 1];
stu[j + 1] = t;
}
}
}
printf(完成排序。请使用命令L查看。\n);
}
这里采用了冒泡排序,平均分从高到低排序,这里有个点比较容易犯错误,在比较完平均分后不能只交换平均分,要对平均分所在的结构体进行交换。
按学号搜索
void Search(Student *stu, int *stunum)
{
int stuid;
printf(请输入要查询的学生学号:);
scanf(%d, &stuid);
for (int i = 0; i < *stunum; i++)
{
if (stu[i].id == stuid)
{
printf(学号 语文 数学 外语 平均分\n);
printf(%d %.1f %.1f %.1f %.1f\n, stu[i].id, stu[i].chinese, stu[i].math, stu[i].english, stu[i].average);
}
}
}
将输入的学号通过for循环和每一个结构体的id成员遍历比较,相等的话就打印出该结构体的所有成员。
不足
这个小项目还有很多可以改进的地方,比如输入的分数是否合法,按学号查找时成绩列表中是否有该学生,这些都是编写过程中没有想到的,后续我会继续完善这些功能。
哔哩哔哩视频
第一次投稿,有不足的地方请大家多多指教