C语言学生结构体总分计算与冒泡排序实现优化及问题排查
C语言学生成绩管理作业代码问题排查与优化
我是C语言初学者,熬夜写完了课程作业,想确认现有实现有没有错误,以及是否存在更高效的写法。
本次作业定义的学生结构体如下:
typedef struct student { int id; int prob_1; int prob_2; int prob_3; int total_score; char grade; }Student
我通过typedef将结构体类型重命名为Student,实现的两个功能函数如下:
void cal_total_score(Student* s) { /* 问题(A): 实现函数"cal_total_score"。 - 每个学生的total_score为prob_1、prob_2、prob_3三项分数之和 - 函数外的代码不得修改 */ int i; for (i = 0; i < NUMSTUDENT; i++) { s[i].total_score = s[i].prob_1 + s[i].prob_2 + s[i].prob_3; } } void student_bb_sort(Student* s) { /* 问题(B): 实现函数"student_bb_sort"。 - 学生数组"s"需按照total_score字段排序 - 必须使用冒泡排序算法 - 函数外的代码不得修改 */ Student tmp; int i, j; for (i = 0; i < NUMSTUDENT - 1; i++) { for (j = 0; j < NUMSTUDENT - 1 - i; j++) { if (s[j].total_score > s[j + 1].total_score) { tmp = s[j]; s[j] = s[j + 1]; s[j + 1] = tmp; } } } }
最初因篇幅限制省略了main函数,现将题目提供的main函数补充如下供参考:
typedef struct student { int id; int prob_1; int prob_2; int prob_3; int total_score; char grade; }Student; void cal_total_score(Student* s); void student_bb_sort(Student* s); int main() { Student s[NUMSTUDENT]; int i; for (i = 0; i < NUMSTUDENT; i++) { s[i].id = i; s[i].prob_1 = rand() % 30 + 1; s[i].prob_2 = rand() % 30 + 1; s[i].prob_3 = rand() % 40 + 1; printf("Student %d\nP1: %d\tP2: %d\tP3: %d\n", s[i].id, s[i].prob_1, s[i].prob_2, s[i].prob_3); } for (i = 0; i < NUMSTUDENT; i++) { cal_total_score(&s[i]); printf("Student %d\tScore: %d\n", s[i].id, s[i].total_score); } student_bb_sort(s); printf("\nAfter Bubble Sort\n"); for (i = 0; i < NUMSTUDENT; i++) { if ((float)i < (float)NUMSTUDENT / 3) { s[i].grade = 'C'; } else if ((float)i < (float)NUMSTUDENT / 3 * 2 && (float)i >= (float)NUMSTUDENT / 3) { s[i].grade = 'B'; } else { s[i].grade = 'A'; } printf("Student %d\tScore: %d\tGrade: %c\n", s[i].id, s[i].total_score, s[i].grade); } }
请帮忙指出代码中存在的错误、危险写法,以及可提升效率的优化方案,谢谢。
现存问题说明
1. 直接导致编译/运行失败的硬错误
- 结构体定义缺少分号:第一次贴出的结构体定义末尾
}Student后没有分号,C语言中结构体typedef定义必须以分号结尾,属于语法错误,会直接编译失败。 cal_total_score函数逻辑和调用方式完全不匹配:main函数中是循环传入单个学生的指针&s[i],但当前实现直接把传入指针当成长度为NUMSTUDENT的数组首地址遍历。当i=0时传入的是数组首地址,循环计算碰巧能覆盖整个数组;但i=1时传入的是s[1]的地址,函数内部访问s[i]就会从s[1]开始往后偏移,最终访问到数组外的野内存,属于越界写入的未定义行为,轻则分数计算完全错误,重则程序直接崩溃。- 依赖项缺失:代码中用到的
NUMSTUDENT宏没有提前定义,使用printf、rand没有引入<stdio.h>、<stdlib.h>头文件,main函数末尾缺少return 0;和闭合大括号,都会导致编译失败。
2. 效率与逻辑严谨性问题
- 冒泡排序缺少提前终止优化:当前实现无论数组是否已经有序,都会固定跑满
NUMSTUDENT-1轮遍历,做了很多无用功。如果某一轮遍历中没有发生任何交换,说明数组已经完全有序,可以直接终止排序。 - 等级判断存在冗余与精度风险:main函数中判断B等级时重复写了
(float)i >= (float)NUMSTUDENT / 3的条件,实际上进入else if分支的前提就是前面的if条件不成立,该判断完全多余;另外大量将整数转成浮点数做比较,不仅有额外的类型转换开销,还可能因为浮点数精度问题出现判断错误,用整数运算实现会更稳妥高效。 - 随机数未设种子:main函数中直接调用
rand()生成分数,没有用srand()设置随机种子,每次运行程序生成的分数序列都是完全一样的,不过这部分属于题目提供的代码,要求不能修改的话可以不用调整。
优化实现(严格遵守函数外代码不修改的要求)
修正后的cal_total_score
因为每次调用传入的是单个学生的指针,不需要循环遍历,直接操作当前传入的学生即可:
void cal_total_score(Student* s) { s->total_score = s->prob_1 + s->prob_2 + s->prob_3; }
如果允许修改main函数的调用逻辑,更高效的写法是只调用一次函数,传入数组首地址,在函数内循环计算所有学生总分,减少多次函数调用的开销,但题目要求函数外代码不能改动,所以以上写法是唯一符合要求的正确实现。
优化后的student_bb_sort
保留原有的升序逻辑(分数从低到高排列,和后续C/B/A的等级评定逻辑匹配),增加交换标记实现提前终止,减少无效遍历:
void student_bb_sort(Student* s) { Student tmp; int i, j; int swapped; // 标记当前轮次是否发生过交换 for (i = 0; i < NUMSTUDENT - 1; i++) { swapped = 0; // 每轮开始前重置标记 for (j = 0; j < NUMSTUDENT - 1 - i; j++) { if (s[j].total_score > s[j + 1].total_score) { tmp = s[j]; s[j] = s[j + 1]; s[j + 1] = tmp; swapped = 1; } } if (!swapped) break; // 本轮无交换,数组已有序,直接退出 } }
main函数可选优化(若允许修改题目提供的代码)
去掉冗余判断和浮点数转换,用整数运算实现等级判定,效率更高也更稳妥:
// 提前计算两个等级分界点,避免循环中重复计算 int tier_c = NUMSTUDENT / 3; int tier_b = NUMSTUDENT * 2 / 3; for (i = 0; i < NUMSTUDENT; i++) { if (i < tier_c) { s[i].grade = 'C'; } else if (i < tier_b) { s[i].grade = 'B'; } else { s[i].grade = 'A'; } printf("Student %d\tScore: %d\tGrade: %c\n", s[i].id, s[i].total_score, s[i].grade); }
内容的提问来源于stack exchange,提问作者cozy_hn
相关产品推荐
相关产品推荐

