前记
这是数据结构课的第一份作业。不得不说老师很有自己的教学思路,这个实验如她所说,贯穿了数据结构课,每到一个章节,或是许多次作业都会让我回想起这个我做了五天的实验。此时是我转入计算机专业后的第一份c语言作业,我在第一次认识了结构体。希望看到这个帖子的你也可以对照实验要求和结果,形成自己的实验思路。
一、实验要求
二、 实验预期效果
三、实验思路
倘若你只是为了作业而来,请直接跳转到末尾复制代码,如果你觉得不错,可以给个赞~谢谢~
我相信你不仅是想要得到作业答案,而是想要获得更多的:譬如遇到这个典型的题目,如何获得解决问题的思路?如果你不幸和我一样没有参加竞赛队,实际上是信息较为匮乏的,不过不必担心,解决这个问题你还是可以的。
拿到题目,我们先看看实验结果,这是最直观的。
首先,我们发现这个“系统”可以实现四个功能,增看改查,即增加一行学生信息,看所有学生信息,修改学生信息,查看一行学生信息。
然后我们还发现了演示的结果图里面有重复的模板内容(这是很关键的一点),我们在C语言中显然是不采取重复的打印操作的,这样写起来代码很多,很难看,执行起来也奇怪。
你可以往上面滑动,看看是哪些地方重复出现了?
思考区
显然,“学生管理系统”以及“功能选择”重复了。于是我们思考建立一个“菜单栏”,放在一个死循环里面,供用户输入数字来一直选择功能。你还可以新增一个退出的选择数字,跳出这个死循环。
由结果图1我们又知道在这个大循环之前,有一个只出现过一次的“学生数量和信息”的输入,于是我们把这个输入放在大循环前面。
思考现在有多少个变量需要用来录入,他们的数据类型是什么?然后我们用scanf获取他们,vs用scanf_s。
接下来是最关键的一步:我们用什么存储表结构?倘若你还没了解线性表,你也可以把C语言教材翻一翻,看看目录,找到关于结构体的语法写法的页面。是的,直接看写法。比起百度,我更推荐你翻书。
接下来照葫芦画瓢,我们获得了一个结构体。接下来我们再找到一个“将结构体信息打印出来”的例子,能打印那就能进行计算成绩等一系列操作了,你可能觉得我在为难你,但是事实正如我所说的。
接下来又是关键一步:我们是否将大量的操作都塞在一个循环里?其实也可以,只不过可能你自己也受不了,明明都是自己写的,却又难以下咽,排查bug的时候有苦说不出。我们这里采用函数,回应题目要求,倘若你难以理解什么是函数,或是知道函数这个概念却不知道和指针怎么搭配使用,或是传递参数这一点有疑惑,我推荐你看看我的代码,可能对你有所帮助。你如果觉得难以阅读,同样可以翻看书上函数部分或是指针部分的例子,找一个简明的例子。
四、代码
#include<stdio.h>
#include<stdbool.h>
struct STUDENT{
int num; //学号
char name[20]; //姓名
char sex[10]; // 性别
int English; //英语
int Computer; //计算机
int Math; //高数
int Total; // 总分
float Average; // 平均分,小数点后两位!!!
}stu[100];//介个是一个类型为 STUDENT 的一维数组 stu,最大为100
void InputStu_lijie(struct STUDENT stu[],int *_n_){ // 输入所有学生的信息,建立学生表
int i;
printf("请输入学生信息:\n");
for(i = 0; i < *_n_; i++){
scanf("%d %s %s %d %d %d",&stu[i].num,stu[i].name,stu[i].sex,&stu[i].English,&stu[i].Computer,&stu[i].Math);//录入
}
return;
}
void OutputStu_lijie(struct STUDENT stu[],int *_n_){ // 输出所有学生的信息
int i;
printf("序号\t学号\t姓名\t性别\t英语\t计算机\t高数\t总分\t平均分\n");
for(i = 0; i < *_n_; i++){
stu[i].Total = stu[i].English+stu[i].Computer+stu[i].Math;
stu[i].Average = stu[i].Total/3.0;
printf("%d\t%d\t%s\t%s\t%d\t%d\t%d\t%d\t%.2f\n",i+1,stu[i].num,stu[i].name,stu[i].sex,
stu[i].English,stu[i].Computer,stu[i].Math,stu[i].Total,stu[i].Average);
}
}
void SearchStu_lijie(struct STUDENT stu[],int *_n_,int u){ // 查找学生信息,有则输出该生信息;否则查找失败
int i;
int f = 0; // f为标志变量
for(i = 0; i < *_n_; i++){
if(stu[i].num==u){
printf("\n查找成功\n\n学号\t姓名\t性别\t英语\t计算机\t高数\t总分\t平均分\n");//注意此处不含序号一列
printf("%d\t%s\t%s\t%d\t%d\t%d\t%d\t%.2f\n",stu[i].num,stu[i].name,stu[i].sex,
stu[i].English,stu[i].Computer,stu[i].Math,stu[i].Total,stu[i].Average);
f = 1;
break; //找到就跳出for
}
}
if(f == 0)printf("该生的信息不存在,查找失败");
return;
}
void UpdateStu_lijie(struct STUDENT stu[],int *_n_){ // 修改:先查找,找到再修改
int u,c,i; // u为待修改学号,c为待修改选项
int f = 0; // f为标志变量
do{
printf("请输入要修改记录的学号:");
scanf("%d",&u);
for(i = 0; i < *_n_; i++){
if(stu[i].num==u){
stu[i].Total = stu[i].English+stu[i].Computer+stu[i].Math;
stu[i].Average = stu[i].Total/3.0;
printf("\n学号\t姓名\t性别\t英语\t计算机\t高数\t总分\t平均分\n"); //注意此处不含序号一列
printf("%d\t%s\t%s\t%d\t%d\t%d\t%d\t%.2f",stu[i].num,stu[i].name,stu[i].sex,
stu[i].English,stu[i].Computer,stu[i].Math,stu[i].Total,stu[i].Average);
f = 1;
break; // 找到就跳出for
}
}if(f==0)printf("该生信息不存在,修改失败");
}while(f==0);
do{
printf("\n\t1.姓名\n\t2.性别\n\t3.英语\n\t4.计算机\n\t5.高数\n\t请选择要修改的信息(1-5):");
scanf("%d",&c);
printf("请输入修改后的信息:");
switch(c){
case 1:
scanf("%s",stu[i].name);break;
case 2:
scanf("%s",stu[i].sex);break;
case 3:
scanf("%d",&stu[i].English);break;
case 4:
scanf("%d",&stu[i].Computer);break;
case 5:
scanf("%d",&stu[i].Math);break;
}
}while(c>5||c<1); //防止非法输入
stu[i].Total = stu[i].English+stu[i].Computer+stu[i].Math;
stu[i].Average = stu[i].Total/3.0;
printf("\n学号\t姓名\t性别\t英语\t计算机\t高数\t总分\t平均分\n");
printf("%d\t%s\t%s\t%d\t%d\t%d\t%d\t%.2f\n",stu[i].num,stu[i].name,stu[i].sex,
stu[i].English,stu[i].Computer,stu[i].Math,stu[i].Total,stu[i].Average); //输出修改后的学生信息
return;
}
void AppendStu_lijie(struct STUDENT stu[],int *_n_){ // 在学生表末尾添加一个记录
scanf("%d %s %s %d %d %d",&stu[*_n_].num,&stu[*_n_].name,&stu[*_n_].sex,&stu[*_n_].English,
&stu[*_n_].Computer,&stu[*_n_].Math); //录入
*_n_ += 1;
return;
}
int main(){
int c,n,u; //c为选项,n为输入个数,u为待查找学号
int *_n_ = &n;
int flag = 0;// 初始化标志变量
do{
printf("请输入学生记录个数n=");
scanf_s("%d",&n);
if(n <= 0 || n > 100){
flag=1;
}
}while(flag != 0); // 防止非法输入
InputStu_lijie(stu,_n_);
do{
printf("\n\t\t\t\t学生信息管理系统\n\t1.显示学生信息\n\t2.查找学生信息\n\t3.修改学生信息\n\t4.添加学生信息\n\t请选择(1-4):");
scanf("%d",&c);
printf("\n");
switch(c){
case 1:
OutputStu_lijie(stu,_n_);break;
case 2:
printf("请输入待查找的学号:");
scanf("%d",&u);
SearchStu_lijie(stu,_n_,u);break;
case 3:
UpdateStu_lijie(stu,_n_);break;
case 4:
if(n == 100){ // n的其他情况前面已考虑,此处考虑数组上限问题
printf("此时记录达到数组stu上限,请重新进行系统功能选项选择!\n");
}else{
printf("请输入学生信息:");
AppendStu_lijie(stu,_n_);
printf("更新成功");
}
break;
default:
printf("请重新进行选择\n");
}
}while(true);
return 0;
}
以上程序在dev c++测试无误。对于超出范围的选项输入,给予了再次输入的机会;对于成绩的错误输入没有给予限制,个人认为由于修改功能的存在,没必要限定成绩输入范围。在vs环境下字符数组的输入应将scanf();更换为scanf_s();对于四个及四个以上汉字的名字输入产生的表格对不齐没有进行处理。
五、题目的隐藏条件
我们拿到题之后,应该注意数据的边界问题。题目要求100个以内的学生信息,那就要限制一开始输入学生个数n的范围是大于零小于一百的。这点或许大多数人都注意到了。但更为隐蔽的是新增学生信息带来的数组上限问题。我们应该增加判断条件,不能让已经达到100的学生表继续新增,倘若上限没有限制,那应该考虑malloc新的数组空间出来赋值,这不在本题的考虑范围内。在测试时,可以先设置一个较小的数字检查是否达到预期。
考虑系统的健壮性问题,避免用户进行了非法输入而导致要重复运行或是结果错误。
列名一行有时是有“序号”,有时是没有的。