C語言中getchar()的原理以及易錯點解析
一.getchar()系列
1.getchar()工作原理及作用
工作原理:getchar()是stdio.h中的庫函數,它的作用是從stdin流中讀入一個字符,也就是說,如果stdin有數據的話不用輸入它就可以直接讀取瞭,第一次getchar()時,確實需要人工的輸入,但是如果你輸瞭多個字符,以後的getchar()再執行時就會直接從緩沖區中讀取瞭。
實際上是 輸入設備->內存緩沖區->getchar()
你按的鍵是放進緩沖區瞭,然後供程序getchar()
你有沒有試過按住很多鍵然後等一會兒會滴滴滴滴響,就是緩沖區滿瞭,你後頭按的鍵沒有存進緩沖區.
鍵盤輸入的字符都存到緩沖區內,一旦鍵入回車,getchar就進入緩沖區讀取字符,一次隻返回第一個字符作為getchar函數的值,如果有循環或足夠多的getchar語句,就會依次讀出緩沖區內的所有字符直到’\n’.要理解這一點,之所以你輸入的一系列字符被依次讀出來,是因為循環的作用使得反復利用getchar在緩沖區裡讀取字符,而不是getchar可以讀取多個字符,事實上getchar每次隻能讀取一個字符.如果需要取消’\n’的影響,可以用getchar();來清除,這裡getchar();隻是取得瞭’\n’但是並沒有賦給任何字符變量,所以不會有影響,相當於清除瞭這個字符.
作用1:從緩沖區讀走一個字符,相當於清除緩沖區。
作用2:前面的scanf()在讀取輸入時會在緩沖區中留下一個字符’\n’(輸入完按回車鍵所致),所以如果不在此加一個getchar()把這個回車符取走的話,接下來的scanf()就不會等待從鍵盤鍵入字符,而是會直接取走這個“無用的”回車符,從而導致讀取有誤。
2.使用getchar()清理回車\n
這個問題轉載自n_s_X14,但是作者在文章最後留瞭一個問題,現在在這裡給大傢解釋一下原因。
文章的源碼為:
#include <stdio.h> int main(void){ char m[40]; char n; printf("please input first str:\n"); //提示用戶輸入第一個字符串 scanf("%s",m); //獲取用戶第一個輸入字符串 printf("you input str is :%s\n",m); //輸出用戶的輸入的第一個字符串 printf("input second char :\n"); //提示用戶輸入第二個字符 scanf("%c",&n); //獲取用戶的第二個字符 printf("now you input second char is :%c\n",n);//輸出用戶輸入的第二個字符 return 0; }
Output:
please input first str:
abc
you input str is :abc
input second char :
now you input second char is :Program ended with exit code: 0
問題:我們第一次輸入abc後成功打印出來瞭you input str is :abc
,但是執行到printf("input second char :\n");
時,還沒等到第二次輸入就打印出來瞭。這是為什麼??
原因:
其實在我們第一次輸入並按下回車的時候,控制臺一共獲得瞭四個字符,分別是:a、b、c、回車(enter)。但是因為scanf()方法遇到非字符的時候會結束從控制臺的獲取,所以在輸入’abc’後,按下 ‘回車(enter)’ 的同時,將’abc’這個值以字符串的形式賦值給瞭類型為 ‘char’ 的 ‘m’ 數組,將使用過後的字符串: ‘回車(enter)’ 保存在控制臺輸入的緩沖區,然後繼續執行下一段輸出代碼,然後又要求用戶輸入。此時,因為上一次被使用過後的字符串被保存在緩沖區,現在scanf()方法從控制臺的緩沖區獲取上一次被使用過後的字符串,並隻截取第一個字符: ‘回車(enter)’ ,此時控制臺緩沖區才算使用完瞭。所以在看似被跳過的輸入,其實已經scanf()方法已經獲取瞭我們的輸入瞭,這個輸入就是一個 ‘回車(enter)’ 。
解決問題:
使用getchar()方法,清除掉abc後面的緩存(回車enter)。
#include <stdio.h> int main(void){ char m[40]; char n; printf("please input first str:\n"); //提示用戶輸入第一個字符串 scanf("%s",m); //獲取用戶第一個輸入字符串 printf("you input str is :%s\n",m); //輸出用戶的輸入的第一個字符串 getchar(); printf("input second char :\n"); //提示用戶輸入第二個字符 scanf("%c",&n); //獲取用戶的第二個字符 printf("now you input second char is :%c\n",n);//輸出用戶輸入的第二個字符 return 0; }
Output:
please input first str:
abc
you input str is :abc
input second char :
de
now you input second char is :d
Program ended with exit code: 0
3.使用getchar()清理緩存
文章結束時留瞭一個問題:如果在第一次輸入ab後加一個空格再回車,又會出現原來的問題,即程序隻輸出瞭ab後就自動跳過下一次的輸入之間退出瞭,控制臺輸出如下圖所示。
原因:
在獲取用戶第一個輸入字符串時,scanf("%s",&m);
,我們用%s作為轉換說明,%s的作用是“把輸入解釋成字符串。從第一個非空白字符開始,到下一個空白字符之前的所有字符都是輸入。”所以scanf把輸入的ab空格+回車
就理解為ab+回車
(ab後面沒有空格),但是依然以ab空格+回車
的形式存儲在緩存區。我們輸入ab空格+回車
,在緩存區是這樣存放的:
其中,第三格存放的為空格鍵。
當程序運行完 getchar();
後,隻清除瞭第三格中的空格鍵,因為一次執行getchar();
隻清除一個緩存,留下瞭第四格中的回車鍵,因此再次出現瞭同樣的問題。
解決問題:那麼就是說隻要運行兩次getchar();
,清除掉第三格和第四格就可以正常瞭。
#include <stdio.h> int main(void){ char m[40]; char n; printf("please input first str:\n"); //提示用戶輸入第一個字符串 scanf("%s",m); //獲取用戶第一個輸入字符串 printf("you input str is :%s\n",m); //輸出用戶的輸入的第一個字符串 getchar(); //第一次清除緩存 getchar(); //第二次清除緩存 printf("input second char :\n"); //提示用戶輸入第二個字符 scanf("%c",&n); //獲取用戶的第二個字符 printf("now you input second char is :%c\n",n);//輸出用戶輸入的第二個字符 return 0; }
Output:
由此可見,當我們第一次輸入ab空格+回車後,程序正常運行。
進一步:如果我們輸入的是a空格b+回車
,scanf("%s",m);
這一步隻能讀取到a,因為a後面有空格。但是a空格b+回車
在緩沖區這樣存放:
因此,如果想要程序正常運行則需要在輸出用戶的輸入的第一個字符串後加入三次getchar();
操作,即刪除掉第二,第三,第四格的內容。
問題:如果我們輸入a空格bbbbbbbb+回車
,那可能需要無數個getchar();
來清除緩存,這時應該怎麼辦??
解決方法:加入while循環while(getchar()!='\n') continue;
#include <stdio.h> int main(void){ char m[40]; char n; printf("please input first str:\n"); //提示用戶輸入第一個字符串 scanf("%s",m); //獲取用戶第一個輸入字符串 printf("you input str is :%s\n",m); //輸出用戶的輸入的第一個字符串 while(getchar()!='\n') //通過while循環刪除緩存 continue; printf("input second char :\n"); //提示用戶輸入第二個字符 scanf("%c",&n); //獲取用戶的第二個字符 printf("now you input second char is :%c\n",n);//輸出用戶輸入的第二個字符 return 0; }
這時,我們輸入a空格bbbbbbbb+回車
,程序正常運行。
解析:
while(getchar()!='\n') continue;
可以看出這段代碼代替瞭無數個getchar(),他的作用是跳過剩餘的輸入行
第一次while循環消除第二格緩存,第二次while循環消除第三格緩存……直到第八次。最後一次同樣,getchar()也消除瞭回車。
while(getchar()!='\n')
可以拆分成兩步,
第一步調用getchar()
方法(這裡getchar();隻是取得瞭’\n’但是並沒有賦給任何字符變量,所以不會有影響,相當於清除瞭這個字符)。
第二步判斷獲取到的緩存是否等於’\n’。
4.混合scanf()與getchar()
假設程序要求用getchar()處理字符輸入,用scanf()處理數值輸入,這兩個函數都能很好的完成任務,但是不能混合使用。 因為getchar()讀取每個字符,包括空格、制表符和換行符;而scanf()在讀取數字時則會跳過空格、制表符和換行符。
例:
要求用戶輸入一個字母和兩個數字,輸出以第一個數字為行數,第二個數字為列數,以字母為內容的數列,要求可以不斷輸入直至鍵入回車退出程序:
#include <stdio.h> void display(char cr,int lines,int width); int main(int argc, const char * argv[]) { int ch; int rows,cols; printf("Enter a character and two integers:\n"); while((ch=getchar())!= '\n'){ scanf("%d %d", &rows,&cols); display(ch, rows, cols); printf("Enter another character and two integers;\n"); printf("Enter a newline to quit.\n"); } printf("Bye.\n"); return 0; } void display(char cr,int lines,int width){ int row,col; for(row=1; row<= lines; row++){ for(col =1; col<=width; col++){ putchar(cr); } putchar('\n'); } }
output:
我們發現,在第一次輸入成功打印後,程序自動退出。這明顯不符合我們的題目要求。
原因是,輸入的c23其實是c23+換行符
,scanf()函數把這個換行符留在瞭緩存中。getchar()不會跳過換行符,所以在進入下一輪迭代時,還沒來得及輸入字符,它就讀取瞭換行符,然後將其賦值給瞭ch。而ch是換行符正式終止循環的條件。
如何改進??
- 我們需要刪除scanf()函數留在緩存中的換行符即可。
- 在if語句中使用一個break語句,可以在scanf()的返回值不等於2時終止程序,即如果一個或兩個輸入值不是整數或者遇到文件結尾就終止程序。
#include <stdio.h> void display(char cr,int lines,int width); int main(int argc, const char * argv[]) { int ch; int rows,cols; printf("Enter a character and two integers:\n"); while((ch=getchar())!= '\n'){ if( scanf("%d %d", &rows,&cols)!=2 ){ break; } display(ch, rows, cols); while(getchar()!='\n'){ continue; } printf("Enter another character and two integers;\n"); printf("Enter a newline to quit.\n"); } printf("Bye.\n"); return 0; } void display(char cr,int lines,int width){ int row,col; for(row=1; row<= lines; row++){ for(col =1; col<=width; col++){ putchar(cr); } putchar('\n'); } }
Output:
題外話:
scanf()中轉化符的問題
問題:從上面兩張圖片中可以看出,當scanf("%d",&c);
改為scanf("%c",&c);
時,控制臺中出現瞭圖二的問題。character為什麼為空白??
原因:
如果格式是%c,那麼任何字符都是它想要的,所以第二個程序中的第二個scanf("%c")會得到‘+’後面的空格’ '。如果格式是%d,則會忽略任何空白字符(空格、回車、制表符等),忽略的意思是,從緩沖區裡刪除,但並不保存;如果遇到數字,則拿出並保存給後面的整數,也就是說%d的時候,scanf想要的字符是數字和空白符。所以第一個程序裡的第二個scanf("%d")忽略掉瞭空格,正確輸入瞭數字。
總結
到此這篇關於C語言中getchar()的原理以及易錯點解析的文章就介紹到這瞭,更多相關C語言 getchar()原理及易錯點內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!