在 C 語言程式設計中,輸入輸出緩衝區的處理始終是初學者容易踩雷的區塊。特別是當 scanf 與 gets(或 fgets)混用時,常會出現「跳過輸入」或「讀取到空行」等詭異現象。本文將透過一個實際案例,深入剖析緩衝區殘留換行符導致的問題,並提供多種解決方案。
請觀察以下程式碼片段:
#include <stdio.h>
#include <stdlib.h>
int main() {
int i = 0, m = 0;
while (scanf("%d", &m)) {
char str[10][10];
for (i = 0; i < 5; i++) {
printf("請輸入第%d 個字符:", i+1);
scanf("%s", str[i]);
}
}
return 0;
}
執行時輸入 3 並按下 Enter,程式卻輸出:
請輸入第1個字符:請輸入第2個字符:1 請輸入第3個字符:2 請輸入第4個字符:34 請輸入第5個字符:3
明明迴圈應該讓使用者輸入 5 次字串,卻在第一次 printf 後直接跳過等待,將後續輸入一次性讀走。若將 scanf("%s", str[i]) 改為 gets(str[i]),情況更糟——首次 gets 直接讀取到空行。
scanf("%d", &m) 讀取整數時,會跳過前導空白字元(空格、Tab、換行),讀完數字後停在後續的換行符(\n)之前,該換行符仍留在標準輸入緩衝區中。
scanf("%s") 自動吞掉了 \n 並繼續讀取後面的內容。gets 遇到 \n 即停止並丟棄 \n,若緩衝區首字元就是 \n,則直接讀到空字串,導致程式邏輯錯亂。| 方法 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|
在 scanf 後加 getchar() | 簡單教學程式 | 極簡單 | 若緩衝區有多個空白字元會失效 |
使用 scanf(" %s", str)(格式字串前加空格) | 需讀取單詞 | 自動跳過所有空白字元 | 無法讀取含空格的整行 |
改用 fgets 統一讀取整行,再用 sscanf 解析 | 生產級程式、需處理含空格輸入 | 徹底掌控緩衝區,安全無殘留 | 代碼稍多 |
C++ 切換 cin >> str | C++ 專案 | 自動處理空白,型別安全 | 需包含 <iostream>,混編 C IO 仍需注意同步 |
gets 已被 C11 標準移除,極易造成緩衝區溢出,務必改用 fgets。fgets(buf, sizeof(buf), stdin) 讀取原始行,再用 sscanf、strtok 或手寫解析器處理,徹底避免緩衝區殘留問題。void clear_stdin(void) {
int c;
while ((c = getchar()) != '\n' && c != EOF);
}
在關鍵 scanf 之後呼叫即可。「輸入跳過」的本質是 scanf 留下換行符、後續讀取函數對換行符處理策略不同。理解緩衝區行為、統一讀取策略、放棄不安全的 gets,才能寫出健壯的 C 語言輸入輸出代碼。