「輸入一個學生成績,判斷優、良、及格」是C語言分支結構教學中最經典的入門案例。本文不僅提供標準代碼,更深入解析分支邏輯優化、邊界處理、擴展性設計與工程化規範,助你從「能跑通」進階到「寫得好」。
#include <stdio.h>
int main() {
int score = 0;
printf("請輸入學生成績 (0-100): ");
if (scanf("%d", &score) != 1) {
printf("輸入錯誤:請輸入整數\n");
return 1;
}
if (score < 0 || score > 100) {
printf("輸入錯誤:成績範圍 0-100\n");
return 1;
}
if (score >= 90) {
printf("等級:優\n");
} else if (score >= 80) {
printf("等級:良\n");
} else if (score >= 60) {
printf("等級:及格\n");
} else {
printf("等級:不及格\n");
}
return 0;
}
利用整除特性將分數映射到case標簽:
switch (score / 10) {
case 10:
case 9: printf("等級:優\n"); break;
case 8: printf("等級:良\n"); break;
case 7:
case 6: printf("等級:及格\n"); break;
default: printf("等級:不及格\n");
}
注意:需先校驗`score`在0-100範圍內,否則負數或>100會進入`default`誤判。
將等級邏輯數據化,便於動態調整閾值:
typedef struct {
int min_score;
const char *grade;
} GradeThreshold;
const GradeThreshold thresholds[] = {
{90, "優"},
{80, "良"},
{60, "及格"},
{0, "不及格"}
};
const int threshold_count = sizeof(thresholds) / sizeof(thresholds[0]);
const char* get_grade(int score) {
for (int i = 0; i < threshold_count; i++) {
if (score >= thresholds[i].min_score)
return thresholds[i].grade;
}
return "無效"; // 理論上不可達
}
// 穩健輸入函數
int read_score(int *out_score) {
char buf[64];
if (!fgets(buf, sizeof(buf), stdin)) return 0; // EOF
char *endptr;
long val = strtol(buf, &endptr, 10);
if (endptr == buf || *endptr != '\n') return 0; // 非純數字
if (val < 0 || val > 100) return 0;
*out_score = (int)val;
return 1;
}
// 單元測試 (簡易版)
void test_grade() {
struct { int score; const char *expected; } cases[] = {
{100, "優"}, {95, "優"}, {90, "優"},
{89, "良"}, {80, "良"},
{79, "及格"}, {60, "及格"},
{59, "不及格"}, {0, "不及格"},
{-1, "無效"}, {101, "無效"}
};
for (int i = 0; i < sizeof(cases)/sizeof(cases[0]); i++) {
const char *got = get_grade(cases[i].score);
if (strcmp(got, cases[i].expected) != 0) {
printf("FAIL: score=%d expected=%s got=%s\n",
cases[i].score, cases[i].expected, got);
}
}
printf("測試完成\n");
}
| 錯誤代碼 | 問題 | 修正 |
|---|---|---|
if (score > 90) | 邊界錯誤:90分應為「優」 | if (score >= 90) |
if (score >= 60) ... else if (score >= 80) | 順序錯誤:80分會被第一條捕獲 | 按降序排列條件:90→80→60 |
scanf("%d", score) | 缺少取地址符 `&` | scanf("%d", &score) |
| 無輸入驗證直接判斷 | 非數字/超範圍導致未定義行為 | 必須檢查`scanf`返回值與範圍 |
| 使用`float`存儲分數 | 浮點比較精度陷阱 | 成績用`int`,必要時用定點數 |
// 結構體封裝學生信息
typedef struct {
char name[32];
int scores[5]; // 5門課
double avg;
const char *level;
} Student;
void evaluate(Student *s) {
int sum = 0;
for (int i = 0; i < 5; i++) sum += s->scores[i];
s->avg = sum / 5.0;
if (s->avg >= 90) s->level = "優秀";
else if (s->avg >= 80) s->level = "良好";
else if (s->avg >= 60) s->level = "及格";
else s->level = "不及格";
}
這個看似簡單的案例,實則蘊含了「分支結構設計、邊界條件處理、數據驅動邏輯、錯誤防禦性編程、可測試性」等工程核心思想。建議初學者:先寫出正確的if-else版本,再嘗試switch、查表法,最後補全輸入驗證與單元測試,體會代碼從「能用」到「好用」的演進。