經常看到類似下例的問題:
int main()
{
char buf1[5]={0};
char buf2[5]={0};
cin >>buf1;
cin.getline(buf2,5); // 這裡似乎不等待輸入
return 0;
}
而解決辦法如下:
int main()
{
char buf1[5]={0};
char buf2[5]={0};
cin >>buf1;
cin.ignore(); // 或者 cin.sync(); 之類的
cin.getline(buf2,5);
return 0;
}
這是為什麼呢?
因為,首先, Windows 上敲一下Enter鍵,實質上是輸入兩個字元:回車符,緊跟着換行符。這兩個字元的 ASCII 碼分别為 0x0D 和 0x0A,一般來說,其C++轉義表示分别為 '\r' 和 '\n'。然後,cin>> 預設是以一個或多個接連的白空格為間隔,cin.getline 預設則以單個換行符(0x0A)為間隔。回車符和換行符都屬于白空格。
這裡不讨論為什麼 Windows 上敲Enter鍵會輸入這兩個字元,以及這兩個字元本來應該對應什麼動作(如果存在“本來應該”這麼一說的話)。
為了展示這個看不見的Enter鍵敲擊,用 istringstream 舉個例子:
void disp(char *buf,int n)
{
for(int i=0;i<n;++i)
printf("0x%02X ",buf[i]);
printf("\n");
}
int main()
{
disp("\r\ns1\r\ns2",9);
// 這相當于在控制台敲Enter鍵,然後敲入s1,然後敲Enter鍵,然後敲入s2
// 0x0D 0x0A 0x73 0x31 0x0D 0x0A 0x73 0x32 0x00
printf("\n");
{
istringstream iss("\r\ns1\r\ns2");
char buf1[5]={0};
char buf2[5]={0};
iss >>buf1;
iss >>buf2;
disp(buf1,5); // 0x73 0x31 0x00 0x00 0x00
disp(buf2,5); // 0x73 0x32 0x00 0x00 0x00
}
printf("\n");
{
istringstream iss("\r\ns1\r\ns2");
char buf1[5]={0};
char buf2[5]={0};
iss.getline(buf1,5);
iss.getline(buf2,5);
disp(buf1,5); // 0x0D 0x00 0x00 0x00 0x00
disp(buf2,5); // 0x73 0x31 0x0D 0x00 0x00
}
return 0;
}
用 cin 的道理是一樣的。差別在于相關的标準庫函數會把 0x0D 轉換為 0x0A,這就相當于Enter鍵最終敲入緩沖區的是接連的兩個換行符。由于這不影響讨論,下文還是用 0x0D 指代。
cin>> 每讀到其所期待的東西後碰到 0x0D,就“斷”一下,緊跟着的 0x0A 還在緩沖裡。此時,如果改用 cin.getline() ,0x0A 立即被讀入,而該間隔符前面沒有字元,于是就有了 getline 已完成卻沒有 get 到 line 的錯覺。而如果沒有改用 cin.getline(),繼續用cin>>,那麼 cin>> 碰到緊跟着的 0x0A 時,這是在還沒讀到其所期待的東西就碰到了白空格,它的反應就是跳過該字元(這正是 cin.ignore(); 要做的),接着繼續去讀其所期待的東西。