scanf 是 C 標準庫中最常用的輸入函數,但其行為充滿微妙細節:格式字串解析、輸入緩衝區殘留、空白字元處理、寬度限制副作用等,稍有不慎即導致程式「卡住」或讀取錯誤資料。本文系統剖析 scanf 的核心機制、常見陷阱與防禦性編程技巧。
EOF(通常為 -1)。格式字串中的空白字元(空格、Tab、換行)具有特殊含義:「跳過輸入流中任意數量(含零個)的空白字元」。
scanf("%d %d", &a, &b); // 等價於 "%d%d"、"%d\t%d"、"%d\n%d"
// 皆可正確讀取 "10 20"、"10\t20"、"10\n20"
例外:%c 指定符不會自動跳過空白,會讀取下一個字元(含空白)。
char c;
scanf("%c", &c); // 輸入 " a",c 會得到 ' '(空格)
scanf(" %c", &c); // 格式字串前加空格,跳過空白,c 得到 'a'
%5d 表示最多讀取 5 個字元作為整數。若輸入超過寬度,多餘字元留在緩衝區,供下一次 scanf 讀取。
int a, b;
scanf("%5d", &a); // 輸入 "123456"
scanf("%d", &b); // b 直接讀到 "6",使用者無機會輸入
printf("a=%d, b=%d\n", a, b); // 輸出 a=12345, b=6
建議:除非極度確定輸入長度,否則避免在 scanf 使用寬度限制。
經典錯誤範例:
int a, status;
printf("輸入整數 (q 結束):");
status = scanf("%d", &a);
while (status == 1) { // 應用 == 而非 =
printf("輸入下一個:");
status = scanf("%d", &a);
}
問題分析:
q,scanf 匹配 %d 失敗,返回 0,q 滯留緩衝區。status == 1 為假,迴圈結束(正常)。while (status = 1)(賦值),status 永遠為 1,進入無限迴圈;且 q 永遠在緩衝區,scanf 永遠失敗,使用者無法再輸入。防禦寫法:
int a;
while (1) {
printf("輸入整數 (非數字結束):");
if (scanf("%d", &a) != 1) {
// 清空緩衝區殘留
int c;
while ((c = getchar()) != '\n' && c != EOF);
break;
}
printf("讀到:%d\n", a);
}
連續呼叫 scanf("%c") 時,上一次輸入的換行符 \n 會被下一次直接讀走。
char c1, c2;
scanf("%c", &c1); // 輸入 'a' + Enter,c1='a','\n' 留在緩衝區
scanf("%c", &c2); // c2 直接讀到 '\n',程式不等待輸入
三種解法:
scanf(" %c", &c2);(推薦,最簡潔)。getchar() 吞掉換行:getchar(); scanf("%c", &c2);。%*c 忽略一字元:scanf("%c%*c", &c1);(不直觀)。%[...] 可讀取指定字元集合,遇到非集合字元停止。
char str[100];
scanf("%[^\n]", str); // 讀取整行(含空格),直到換行符
// 需搭配前導空格清理殘留換行:scanf(" %[^\n]", str);
格式字串中的字面字元(如逗號)必須精確匹配:
int a, b;
scanf("%d,%d", &a, &b); // 必須輸入 "10,20","10, 20" 會失敗(空格不匹配)
scanf("%d ,%d", &a, &b); // 允許 "10,20"、"10 ,20"、"10 , 20"
// 模式 1:精確匹配項目數
if (scanf("%d %d", &a, &b) == 2) { /* 成功 */ }
// 模式 2:迴圈讀取直到 EOF 或錯誤
while (scanf("%d", &x) == 1) {
// 處理 x
}
// 模式 3:讀取整行再用 sscanf 解析(更健壯)
char line[256];
while (fgets(line, sizeof(line), stdin)) {
if (sscanf(line, "%d %d", &a, &b) == 2) { /* 成功 */ }
}
%c、%[...] 等不自動跳過空白的指定符。%5d),防止緩衝區殘留導致後續讀取異常。while ((c=getchar())!='\n' && c!=EOF);。fgets + sscanf,將「讀取」與「解析」分離,更易錯誤處理。-Wall -Wextra,捕捉格式字串與參數不匹配。理解 scanf 的緩衝區語義與格式解析規則,是寫出健壯 C 程式的關鍵一步。建議在教學或生產環境中,漸進式引導開發者從 scanf 遷移至 fgets/sscanf 組合,徹底規避緩衝區殘留帶來的諸多難以除錯的問題。