初學者常遇到「連續兩次輸入字元,第二次被跳過」的問題。這源於 C 的 scanf、C++ 的 cin 對換行符處理方式不同。本文透過實驗對比,深入解析輸入緩衝區機制與最佳實踐。
scanf("%c") 失敗
#include <stdio.h>
int main() {
char a, b;
printf("Please input the first character: ");
scanf("%c", &a);
printf("Please input the second character: ");
scanf("%c", &b);
printf("The two characters are %c, %c", a, b);
return 0;
}
執行結果:
Please input the first character: A Please input the second character: The two characters are A,
第二次 scanf 直接讀取殘留在緩衝區的 換行符 '\n',不等待使用者輸入。
scanf("%c") 後接 scanf("%d") 正常
#include <stdio.h>
int main() {
char a; int b;
printf("Please input the first character: ");
scanf("%c", &a);
printf("Please input the second integer: ");
scanf("%d", &b);
printf("The two characters are %c, %d", a, b);
return 0;
}
執行結果:
Please input the first character: A Please input the second integer: 123 The two characters are A, 123
%d 會自動跳過空白字元(含換行符、空格、Tab),直到遇到數字才開始解析。
自訂類別 b 的 operator>> 中常見清理緩衝區模式:
iostream& operator>>(iostream& is, b& s) {
char temp[b::MAX] = {0};
is.get(); // 讀取並丟棄一字元(通常是前殘留的 '\n')
is.get(temp, b::MAX); // 讀取最多 MAX-1 字元至 temp
if (strlen(temp) > 0) {
strncpy(s.str, temp, b::MAX);
}
// 清理多餘輸入直到換行符
while (is && is.get() != '\n') {
continue;
}
return is;
}
驗證: 輸入超過 MAX 長度字串,第二次 cin >> b2 會直接讀取殘留的超長部分,證明 while 迴圈確實清理了緩衝區。
cin >> char
#include <iostream>
using namespace std;
int main() {
char a, b;
cout << "Please input the first character: ";
cin >> a;
cout << "Please input the second character: ";
cin >> b;
cout << a << "," << b;
return 0;
}
結果: 正常運作,operator>>(char&) 自動跳過空白字元(含換行符)。
輸入:ABCD 結果:A,B
cin >> a 讀取 'A',cin >> b 讀取 'B','C' 'D' 殘留在緩衝區供下次讀取。
| 操作 | C: scanf("%c") | C: scanf("%d/%s") | C++: cin >> char | C++: cin >> int/string |
|---|---|---|---|---|
| 讀取換行符 | 是 | 否(跳過) | 否(跳過) | 否(跳過) |
| 殘留換行符 | 留在緩衝區 | 留在緩衝區 | 留在緩衝區 | 留在緩衝區 |
| 下次讀取影響 | 直接讀到 '\n' | 自動跳過 | 自動跳過 | 自動跳過 |
scanf("%c", &c) 前加空格:scanf(" %c", &c) 跳過空白字元getchar() 讀取並丟棄換行符%d/%s 後接 %c 需手動清理緩衝區cin >> var,自動處理空白字元getline(cin, str)cin >> 與 getline 時,先 cin.ignore() 清理殘留換行符operator>> 時,參考標準庫實作正確處理緩衝區
// C
while (getchar() != '\n'); // 清空至換行符
// C++
cin.ignore(numeric_limits::max(), '\n'); // 標準寫法
// 或
cin.sync(); // 某些實作上相當於清空輸入緩衝區
標準輸入為行緩衝:使用者輸入一行按 Enter,資料連同 \n 進入核心緩衝區,再由執行庫讀取。關鍵差異在於格式化讀取函數如何處理空白字元:
scanf("%c"):不跳過任何字元,逐字元讀取scanf("%d/%s")、cin >> arithmetic/string:預設跳過前導空白字元(isspace() 為真)cin.get()/getline():不跳過,原始讀取理解輸入緩衝區與空白字元處理規則,是避免「輸入被跳過」Bug 的關鍵。C++ iostream 設計更一致(預設跳過空白),但混用格式化/非格式化讀取時仍需留意殘留換行符。養成「讀取後清理」或「讀取前跳過」的防禦性習慣,能消除此類問題。