5 名學生,信息含:學號、姓名、三門課成績(C++、高數、英語)。要求:
核心考點:結構體數組、字符串輸入、模塊化函數設計、浮點數精度。
#include <stdio.h>
struct Student {
char num[12]; // 學號
char name[15]; // 姓名
float cpp; // C++
float math; // 高數
float english; // 英語
float sum; // 總分
float avg; // 平均分
};
void calculate(struct Student stu[], int n);
int main() {
struct Student students[5];
int i;
printf("請輸入5位學生信息 (學號 姓名 英語 高數 C++):\n");
for (i = 0; i < 5; i++) {
// 注意:%s 讀取到空白符,姓名含空格需用 fgets 或掃描集
scanf("%11s %14s %f %f %f",
students[i].num,
students[i].name,
&students[i].english,
&students[i].math,
&students[i].cpp);
}
calculate(students, 5);
return 0;
}
void calculate(struct Student stu[], int n) {
for (int i = 0; i < n; i++) {
stu[i].sum = stu[i].math + stu[i].english + stu[i].cpp;
stu[i].avg = stu[i].sum / 3.0f;
}
printf("\n%-12s %-15s %8s %8s\n", "學號", "姓名", "總分", "平均分");
for (int i = 0; i < n; i++) {
printf("%-12s %-15s %8.2f %8.2f\n",
stu[i].num, stu[i].name, stu[i].sum, stu[i].avg);
}
}
| 方法 | 代碼 | 優點 | 缺點 |
|---|---|---|---|
scanf("%s") | scanf("%14s", name) | 簡單 | 遇空格截斷,無法讀「張 三」 |
scanf("%[^\n]") | scanf(" %14[^\n]", name) | 讀整行含空格 | 需處理殘留換行符 |
fgets | fgets(name, 15, stdin) | 安全、標準 | 含換行符需手動去除 |
建議:教學簡化用 %s,實戰必用 fgets + 去換行。
// 數組大小 12 → 最大讀 11 字符 + \0
scanf("%11s", stu[i].num); // %11s 限制讀取寬度
// 名稱同理 %14s
原代碼無寬度限制,輸入超長字符串會導致緩衝區溢出,嚴重安全漏洞。
sum 優於 score)。
printf("%.2f", avg); // 保留兩位小數,符合成績單習慣
// 原代碼 %f 輸出 6 位小數(如 85.333333),閱讀性差
struct Student *allocate_students(int n) {
struct Student *stu = malloc(n * sizeof(struct Student));
if (!stu) { perror("內存不足"); exit(1); }
return stu;
}
void free_students(struct Student *stu) { free(stu); }
// 主程序
int n;
printf("學生人數: "); scanf("%d", &n);
struct Student *students = allocate_students(n);
// ... 輸入計算輸出 ...
free_students(students);
bool read_student(struct Student *s) {
printf("學號 姓名 英語 高數 C++: ");
// 讀整行再解析,避免 scanf 失敗留下垃圾
char line[256];
if (!fgets(line, sizeof(line), stdin)) return false;
// 解析:學號 姓名 三門分數
// 假設姓名無空格,簡化用 sscanf
if (sscanf(line, "%11s %14s %f %f %f",
s->num, s->name, &s->english, &s->math, &s->cpp) != 5) {
printf("格式錯誤,請重新輸入\n");
return false;
}
// 分數範圍檢查
if (s->english < 0 || s->english > 100 ||
s->math < 0 || s->math > 100 ||
s->cpp < 0 || s->cpp > 100) {
printf("分數必須在 0-100 之間\n");
return false;
}
return true;
}
學生成績管理是結構體數組、字符串處理、模塊化設計、輸入驗證的綜合實戰。關鍵收穫:
scanf 寬度限制 (%11s) 是防溢出底線;%.2f 符合業務習慣;malloc 突破固定規模限制;fgets + sscanf 組合,健壯性遠超 scanf。