实验要求
设计一种结构体,用来保存学生信息。假定学生的总人数不超过100人。
学生的信息包括学号(<10000的整数)、姓名(长度不超过19的字符串)、三科成绩(整数)以及综合成绩(实数)
除综合成绩外,各项数据均由键盘输入。综合成绩在计算前,其值为0。现在要求实现如下几种功能,分别有对应的操作方式:
1)输入若干学生的信息;对应的操作为:在第一行中输入1和整数N,N代表要输入的学生人数。其后是N行数据,每行数据代表一个学生的信息。
2)计算所有学生的综合成绩;综合成绩=第1科成绩*r1+第2科成绩*r2+第3科成绩*r3。对应的操作为:在一行中输入2和三个实数r1、r2、r3,其中r1、r2和r3代表计算综合成绩时的加权系数,输入保证时rl + r2+ r3 =1.0。
3)插入学生信息;对应的操作为:在第一行中输入3和整数N,N表示要将新输入的学生信息插入到原来的第N个学生前面。如果N大于当前学生人数,则插入到最后面。第二行数据代表要插入的一个学生的信息。
4)删除学生;对应的操作为:在第一行中输入4和整数N,N表示将要删除的学生的序号(从1开始)。如果N大于当前的学生人数,则不会删去学生信息。
5)查询学生;对应的操作为:在第一行中输入5和学生的学号,如果能查询到对应的学生,则在一行中显示学生的信息,如果查询不到,则显示“NotFound.”。还有一种查询方式为按姓名查询,操作如下:在第一行中输入6和学生的姓名,如果能查询到对应的学生,则在一行中显示学生的信息,如果查询不到,则显示“Not Found.”。
6)显示学生信息;对应的操作为:在第一行中输入7,会显示所有学生的信息,每位学生的信息占一行。
7)退出系统;对应的操作为:在第一行中输入0。
分析实验
首先需要设计一种结构体,用来保存学生信息,题目中又说假定学生的总人数不超过100人,那么我们可以用结构体数组来进行保存学生信息,如果没有规定学生的总人数那我们就只能使用链表去实现了,结构体如下:
typedef struct{
int num; // 学号
char name[20]; // 姓名
int a; // 三科成绩
int b; // 三科成绩
int c; // 三科成绩
float score = 0.0; // 综合成绩
} student;
// 声明结构体数组用来保存学生信息
student stu[101];
// 声明当前结构体数组下标
int cnt = 0;
我们可以看到每项功能的实现首先是输入不同的数字进行功能选择,想到这里自然联想到switch-case的语法功能,将这个选择判断的实现通过switch-case的方式去实现:
int main() {
int choice, N;
float r1, r2, r3;
while(1) {
scanf("%d", &choice);
switch(choice) {
case 1: { // 增加学生信息
break;
}
case 2: { // 计算综合成绩
break;
}
case 3: { // 插入学生信息
break;
}
case 4: { // 删除学生信息
break;
}
case 5: { // 查找学生通过学号
break;
}
case 6: { // 查找学生通过姓名
break;
}
case 7: { // 显示全部学生信息
break;
}
case 0: { // 退出
break;
}
default : { // 输入错误
break;
}
}
}
return 0;
}
开始实验
1.增加学生信息
首先实现第一个功能,输入若干学生的信息,即增加学生信息,对应的操作为:在第一行中输入1和整数N,N代表要输入的学生人数,其后是N行数据,每行数据代表一个学生的信息:
case 1: { // 输入学生信息
int num, a, b, c;
char name[20];
printf("请输入你增加学生人数:");
scanf("%d", &N);
printf("请按照 学号 姓名 成绩1 成绩2 成绩3 进行输入\n");
for(int i = 0; i < N; i++) {
// 读入数据
scanf("%d %s", &num, name);
scanf("%d", &a);
scanf("%d", &b);
scanf("%d", &c);
if(cnt <= 100) { // 加入判断防止student数组溢出
stu[cnt].num = num;
strcpy (stu[cnt].name, name);
stu[cnt].a = a;
stu[cnt].b = b;
stu[cnt].c = c;
cnt++;
} else {
printf("已经超过数组存储上限");
}
}
break;
}
C语言对于字符串的处理非常粗糙,这里因为学生的名字类型是char数组类型,存储到结构体数组中时使用了头文件string.h中的strcpy()方法
2.计算综合成绩
计算所有学生的综合成绩,综合成绩 = 第1科成绩 * r1+第2科成绩 * r2+第3科成绩 * r3。对应的操作为:在一行中输入2和三个实数r1、r2、r3,其中r1、r2和r3代表计算综合成绩时的加权系数,输入保证时rl + r2+ r3 =1.0,这一功能的实现比较简单,for循环遍历所有已经存放了数据的学生信息,然后按照公式计算出综合成绩进行存储即可:
case 2: { // 计算综合成绩
printf("请输入r1 r2 r3,请确保r1 + r2 + r3 = 1.0\n");
scanf("%f %f %f", &r1, &r2, &r3);
for(int i = 0; i < cnt; i++) {
stu[i].score = stu[i].a * r1 + stu[i].b * r2 + stu[i].c * r3;
}
break;
}
3.插入学生信息
对应的操作为:在第一行中输入3和整数N,N表示要将新输入的学生信息插入到原来的第N个学生前面。如果N大于当前学生人数,则插入到最后面,第二行数据代表要插入的一个学生的信息。因为存储学生信息是通过数组,所以插入学生信息需要将对应位置的学生信息全部向后移动一个位置,如果使用链表操作插入和删除对应学生信息将会更方便,但是数组向末尾插入和查找对应下标的学生信息更方便,这是两种数据结构的不同所产生的问题,代码如下:
// 从index以后的都向后移动一个位置
void moveToBack(int index) {
for(int i = cnt, j = cnt - 1; i >= index; i--, j--) {
stu[i].num = stu[j].num;
strcpy (stu[i].name, stu[j].name);
stu[i].a = stu[j].a;
stu[i].b = stu[j].b;
stu[i].c = stu[j].c;
stu[i].score = stu[j].score;
}
cnt++;
}
case 3: { // 插入学生信息
printf("请输入想要插入的位置:");
scanf("%d", &N);
int num, a, b, c;
char name[20];
if(N >= cnt) {
printf("该位置已经超过了目前最后的位置,将自动插入到最后的位置");
// 读入数据
printf("请按照 学号 姓名 成绩1 成绩2 成绩3 进行输入\n");
scanf("%d %s", &num, name);
scanf("%d", &a);
scanf("%d", &b);
scanf("%d", &c);
if(cnt <= 100) { // 加入判断防止student数组溢出
stu[cnt].num = num;
strcpy (stu[cnt].name, name);
stu[cnt].a = a;
stu[cnt].b = b;
stu[cnt].c = c;
cnt++;
} else {
printf("已经超过数组存储上限");
}
} else {
// 读入数据
printf("请按照 学号 姓名 成绩1 成绩2 成绩3 进行输入\n");
scanf("%d %s", &num, name);
scanf("%d", &a);
scanf("%d", &b);
scanf("%d", &c);
moveToBack(N - 1);
if(cnt <= 100) { // 加入判断防止student数组溢出
stu[N - 1].num = num;
strcpy (stu[N - 1].name, name);
stu[N - 1].a = a;
stu[N - 1].b = b;
stu[N - 1].c = c;
} else {
printf("已经超过数组存储上限");
}
}
break;
}
4.删除学生
对应的操作为:在第一行中输入4和整数N,N表示将要删除的学生的序号(从1开始)。如果N大于当前的学生人数,则不会删去学生信息。删除学生操作需要将对应下标后位置的所有学生向前移动一个位置,代码如下:
// 从index以后的都向前移动一个位置
void moveToFront(int index) {
for(int i = index - 1, j = index; i < cnt; i++, j++) {
stu[i].num = stu[j].num;
strcpy (stu[i].name, stu[j].name);
stu[i].a = stu[j].a;
stu[i].b = stu[j].b;
stu[i].c = stu[j].c;
stu[i].score = stu[j].score;
}
cnt--;
}
case 4: { // 删除学生信息
printf("请输入想要删除的位置:");
scanf("%d", &N);
if(N >= cnt) {
printf("该位置已经超过了存储的学生的最大位置,请重新选择操作");
} else {
moveToFront(N);
}
break;
}
5.查询学生
对应的操作为:在第一行中输入5和学生的学号,如果能查询到对应的学生,则在一行中显示学生的信息,如果查询不到,则显示“Not Found.”,功能实现仅需要遍历所有的学生的学号,如果相等,返回对应学生的信息,如果遍历结束没有学号相等的学生,则输出“Not Found.”,代码如下:
// 学生的个人详细信息
void stuInfo(int index) {
printf("%d %s %d %d %d %.2f\n", stu[index].num, stu[index].name, stu[index].a,
stu[index].b, stu[index].c, stu[index].score);
}
// 通过学号查找学生
void queryStuByNum(int num) {
int flag = 0; // 设置标记是否查找到
for(int i = 0; i < cnt; i++) {
if(stu[i].num == num) {
flag = 1;
stuInfo(i);
}
}
if(flag == 0) {
printf("Not Found\n");
}
}
case 5: { // 查找学生通过学号
printf("请输入想要查找的学生学号:");
int num;
scanf("%d", &num);
queryStuByNum(num);
break;
}
还有一种查询方式为按姓名查询,操作如下:在第一行中输入6和学生的姓名,如果能查询到对应的学生,则在一行中显示学生的信息,如果查询不到,则显示“Not Found.”,功能实现与通过学号查找学生信息类似,将所有的学生信息遍历,如果学生姓名与输入的相同,这里比较学生姓名使用的是string.h头文件中的strmp(str1, str2)函数,当str1与str2相等时返回0,则返回对应学生信息,否则输出“Not Found.”,代码如下:
// 学生的个人详细信息
void stuInfo(int index) {
printf("%d %s %d %d %d %.2f\n", stu[index].num, stu[index].name, stu[index].a,
stu[index].b, stu[index].c, stu[index].score);
}
// 通过姓名查找学生
void queryStuByName(char *name) {
int flag = 0; // 设置标记是否查找到
for(int i = 0; i < cnt; i++) {
if(strcmp(stu[i].name, name) == 0) { // 串比较 相等返回值为0
flag = 1;
stuInfo(i);
}
}
if(flag == 0) {
printf("Not Found\n");
}
}
case 6: { // 查找学生通过姓名
printf("请输入想要查找的学生姓名:");
char name[20];
scanf("%s", name);
queryStuByName(name);
break;
}
6.显示学生信息
对应的操作为:在第一行中输入7,会显示所有学生的信息,每位学生的信息占一行,循环遍历,控制好输出格式即可,代码如下:
// 显示全部学生信息
void toString() {
for(int i = 0; i < cnt; i++) {
printf("%d %s %d %d %d %.2f\n", stu[i].num, stu[i].name, stu[i].a,
stu[i].b,stu[i].c, stu[i].score);
}
}
case 7: { // 显示全部学生信息
toString();
break;
}
7.退出系统
对应的操作为:在第一行中输入0,因为使用了while(1)进行了死循环,所以退出系统使用了头文件stdlib.h中的exit()函数,代码如下:
case 0: { // 退出
exit(0);
break;
}
最终呈现
最后加上必要的输入提示语和菜单选项,再将输出语句修改得更加友好,最终代码如下:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct{
int num; // 学号
char name[20]; // 姓名
int a; // 三科成绩
int b; // 三科成绩
int c; // 三科成绩
float score = 0.0; // 综合成绩
} student;
// 声明结构体数组用来保存学生信息
student stu[101];
// 声明当前结构体数组下标
int cnt = 0;
// 显示选择菜单
void showMenu() {
printf("请输入对应的数字选择对应功能:\n");
printf("1.输入学生信息\n");
printf("2.计算所有学生的综合成绩\n");
printf("3.插入学生信息\n");
printf("4.删除学生信息\n");
printf("5.输入学号查找学生信息\n");
printf("6.输入学生姓名查找学生信息\n");
printf("7.显示全部学生信息\n");
printf("0.退出系统\n");
}
// 显示全部学生信息
void toString() {
for(int i = 0; i < cnt; i++) {
printf("学号:%d 姓名:%s 成绩1:%d 成绩2:%d 成绩3:%d 综合成绩:%.2f\n", stu[i].num, stu[i].name,
stu[i].a, stu[i].b, stu[i].c, stu[i].score);
}
}
// 学生的个人详细信息
void stuInfo(int index) {
printf("学号:%d 姓名:%s 成绩1:%d 成绩2:%d 成绩3:%d 综合成绩:%.2f\n", stu[index].num, stu[index].name,
stu[index].a, stu[index].b, stu[index].c, stu[index].score);
}
// 从index以后的都向后移动一个位置
void moveToBack(int index) {
for(int i = cnt, j = cnt - 1; i >= index; i--, j--) {
stu[i].num = stu[j].num;
strcpy (stu[i].name, stu[j].name);
stu[i].a = stu[j].a;
stu[i].b = stu[j].b;
stu[i].c = stu[j].c;
stu[i].score = stu[j].score;
}
cnt++;
}
// 从index以后的都向前移动一个位置
void moveToFront(int index) {
for(int i = index - 1, j = index; i < cnt; i++, j++) {
stu[i].num = stu[j].num;
strcpy (stu[i].name, stu[j].name);
stu[i].a = stu[j].a;
stu[i].b = stu[j].b;
stu[i].c = stu[j].c;
stu[i].score = stu[j].score;
}
cnt--;
}
// 通过学号查找学生
void queryStuByNum(int num) {
int flag = 0; // 设置标记是否查找到
for(int i = 0; i < cnt; i++) {
if(stu[i].num == num) {
flag = 1;
stuInfo(i);
}
}
if(flag == 0) {
printf("Not Found\n");
}
}
// 通过姓名查找学生
void queryStuByName(char *name) {
int flag = 0; // 设置标记是否查找到
for(int i = 0; i < cnt; i++) {
if(strcmp(stu[i].name, name) == 0) { // 串比较 相等返回值为0
flag = 1;
stuInfo(i);
}
}
if(flag == 0) {
printf("Not Found\n");
}
}
int main() {
int choice, N;
float r1, r2, r3;
while(1) {
showMenu();
scanf("%d", &choice);
switch(choice) {
case 1: { // 输入学生信息
int num, a, b, c;
char name[20];
printf("请输入你增加学生人数:");
scanf("%d", &N);
printf("请按照 学号 姓名 成绩1 成绩2 成绩3 进行输入\n");
for(int i = 0; i < N; i++) {
// 读入数据
scanf("%d %s", &num, name);
// scanf("%s", name);
scanf("%d", &a);
scanf("%d", &b);
scanf("%d", &c);
if(cnt <= 100) { // 加入判断防止student数组溢出
stu[cnt].num = num;
strcpy (stu[cnt].name, name);
// stu[cnt].name = name;
stu[cnt].a = a;
stu[cnt].b = b;
stu[cnt].c = c;
cnt++;
} else {
printf("已经超过数组存储上限");
}
}
// toString(); // 测试
break;
}
case 2: { // 计算综合成绩
printf("请输入r1 r2 r3,请确保r1 + r2 + r3 = 1.0\n");
scanf("%f %f %f", &r1, &r2, &r3);
for(int i = 0; i < cnt; i++) {
stu[i].score = stu[i].a * r1 + stu[i].b * r2 + stu[i].c * r3;
}
break;
}
case 3: { // 插入学生信息
printf("请输入想要插入的位置:");
scanf("%d", &N);
int num, a, b, c;
char name[20];
if(N >= cnt) {
printf("该位置已经超过了目前最后的位置,将自动插入到最后的位置");
// 读入数据
printf("请按照 学号 姓名 成绩1 成绩2 成绩3 进行输入\n");
scanf("%d %s", &num, name);
// scanf("%s", name);
scanf("%d", &a);
scanf("%d", &b);
scanf("%d", &c);
if(cnt <= 100) { // 加入判断防止student数组溢出
stu[cnt].num = num;
strcpy (stu[cnt].name, name);
// stu[cnt].name = name;
stu[cnt].a = a;
stu[cnt].b = b;
stu[cnt].c = c;
cnt++;
} else {
printf("已经超过数组存储上限");
}
} else {
// 读入数据
printf("请按照 学号 姓名 成绩1 成绩2 成绩3 进行输入\n");
scanf("%d %s", &num, name);
scanf("%d", &a);
scanf("%d", &b);
scanf("%d", &c);
moveToBack(N - 1);
if(cnt <= 100) { // 加入判断防止student数组溢出
stu[N - 1].num = num;
strcpy (stu[N - 1].name, name);
stu[N - 1].a = a;
stu[N - 1].b = b;
stu[N - 1].c = c;
} else {
printf("已经超过数组存储上限");
}
}
break;
}
case 4: { // 删除学生信息
printf("请输入想要删除的位置:");
scanf("%d", &N);
if(N >= cnt) {
printf("该位置已经超过了存储的学生的最大位置,请重新选择操作");
} else {
moveToFront(N);
}
break;
}
case 5: { // 查找学生通过学号
printf("请输入想要查找的学生学号:");
int num;
scanf("%d", &num);
queryStuByNum(num);
break;
}
case 6: { // 查找学生通过姓名
printf("请输入想要查找的学生姓名:");
char name[20];
scanf("%s", name);
queryStuByName(name);
break;
}
case 7: { // 显示全部学生信息
toString();
break;
}
case 0: { // 退出
exit(0);
break;
}
default : { // 输入错误
printf("输入错误,请重新选择功能\n");
break;
}
}
}
return 0;
}
总结和存在的问题
1.因为对C语言的输入操作不太熟悉,对于输入的判断没有做,如果输入的数据存在问题,整个程序会崩溃;
2.对于一些边界值的考虑比较简单,也没有进行测试;
3.使用链表也可以完成该实验,但是时间原因没有进行尝试,以后有机会可以使用链表来进行该实验的编写,应该会有不一样的收获。