初學 C 語言時常遇到:程序運行到 scanf 輸入數字後按 Enter,程序卡住、無輸出、或直接跳過輸入。本文從緩衝區機制、格式字符串陷阱、跨平台差異三個維度徹底講透 scanf 輸入「無反應」的根因與標準化解法。
int x, y, z;
printf("請輸入三個整數:");
scanf("%d%d%d", &x, &y, &z); // 輸入 "1 2 3" 後按 Enter,程序卡住無後續輸出
printf("最大值:%d\n", max);
scanf("%d%d%d", ...) 要求三個整數之間必須有空白字符分隔(空格、Tab、換行)。若用戶輸入 123(無分隔),scanf 會將整個 123 讀為第一個 %d,導致後兩個 %d 陷入等待,程序看似「卡住」。
scanf("%d %d %d", &x, &y, &z); // 格式串中空格 = 匹配任意長度空白(含換行)
// 或
scanf("%d,%d,%d", &x, &y, &z); // 要求用戶輸入 "1,2,3" 逗號分隔
若代碼中混用 scanf("%d", &x) 和 scanf("%c", &ch) / gets() / fgets():
int x; char c;
scanf("%d", &x); // 輸入 "10" + Enter → 緩衝區剩 '\n'
scanf("%c", &c); // 直接讀走 '\n',不等待用戶輸入!
#define CLEAR_BUFFER() do { int c; while ((c = getchar()) != '\n' && c != EOF); } while(0)
// 使用示例
scanf("%d", &x);
CLEAR_BUFFER(); // 清空殘留換行符
scanf("%c", &c); // 正常等待字符輸入
scanf 返回成功匹配的項目數。若輸入非數字(如 "abc"),scanf("%d", &x) 返回 0,緩衝區內容不變,下次循環再次讀取同樣失敗,陷入無限循環。
int x;
while (1) {
printf("請輸入整數:");
int ret = scanf("%d", &x);
if (ret == 1) break; // 成功讀取 1 個項目
if (ret == EOF) exit(1); // 文件結束/錯誤
// ret == 0:匹配失敗,清理緩衝區重試
CLEAR_BUFFER();
printf("輸入無效,請重新輸入。\n");
}
| 場景 | Windows (MSVC) | Linux (GCC) |
|---|---|---|
| 行結束符 | \r\n (CRLF) | \n (LF) |
| scanf 讀取換行 | 格式串空格自動跳過 \r\n | 格式串空格自動跳過 \n |
| 控制台緩衝 | 行緩衝,Enter 觸發 | 行緩衝,Enter 觸發 |
| EOF 觸發 | Ctrl+Z 後 Enter | Ctrl+D |
建議:格式串始終包含空格 "%d %d",自動兼容雙平台空白字符。
#include <stdio.h>
#define CLEAR_BUFFER() do { int c; while ((c = getchar()) != '\n' && c != EOF); } while(0)
int main(void) {
int x, y, z, max;
printf("請輸入三個整數(空格分隔):");
// 健壯讀取三個整數
while (scanf("%d %d %d", &x, &y, &z) != 3) {
CLEAR_BUFFER();
printf("輸入格式錯誤,請重新輸入三個整數:");
}
// 求最大值
max = x;
if (y > max) max = y;
if (z > max) max = z;
printf("最大值:%d\n", max);
return 0;
}
完全避開 scanf 緩衝區陷阱的現代寫法:
char line[256];
if (fgets(line, sizeof(line), stdin)) {
int x, y, z;
if (sscanf(line, "%d %d %d", &x, &y, &z) == 3) {
// 成功解析
}
}
// 或更安全的 strtol 逐個解析
"%d %d %d" 而非 "%d%d%d"。scanf 後必須 CLEAR_BUFFER()。scanf(...) != 項目數 觸發清理重試。scanf("%d", &x) 而非 scanf("%d", x)。char s[20]; scanf("%19s", s) 限制寬度。掌握緩衝區機制與返回值檢查,scanf 輸入「無反應」問題將不再困擾。建議新手階段統一使用 fgets + sscanf 模式,徹底規避 scanf 歷史包袱。