C語(yǔ)言中g(shù)etchar()的原理以及易錯(cuò)點(diǎn)解析
一.getchar()系列
1.getchar()工作原理及作用
工作原理:getchar()是stdio.h中的庫(kù)函數(shù),它的作用是從stdin流中讀入一個(gè)字符,也就是說(shuō),如果stdin有數(shù)據(jù)的話不用輸入它就可以直接讀取了,第一次getchar()時(shí),確實(shí)需要人工的輸入,但是如果你輸了多個(gè)字符,以后的getchar()再執(zhí)行時(shí)就會(huì)直接從緩沖區(qū)中讀取了。
實(shí)際上是 輸入設(shè)備->內(nèi)存緩沖區(qū)->getchar()
你按的鍵是放進(jìn)緩沖區(qū)了,然后供程序getchar()
你有沒(méi)有試過(guò)按住很多鍵然后等一會(huì)兒會(huì)滴滴滴滴響,就是緩沖區(qū)滿了,你后頭按的鍵沒(méi)有存進(jìn)緩沖區(qū).
鍵盤輸入的字符都存到緩沖區(qū)內(nèi),一旦鍵入回車,getchar就進(jìn)入緩沖區(qū)讀取字符,一次只返回第一個(gè)字符作為getchar函數(shù)的值,如果有循環(huán)或足夠多的getchar語(yǔ)句,就會(huì)依次讀出緩沖區(qū)內(nèi)的所有字符直到’\n’.要理解這一點(diǎn),之所以你輸入的一系列字符被依次讀出來(lái),是因?yàn)檠h(huán)的作用使得反復(fù)利用getchar在緩沖區(qū)里讀取字符,而不是getchar可以讀取多個(gè)字符,事實(shí)上getchar每次只能讀取一個(gè)字符.如果需要取消’\n’的影響,可以用getchar();來(lái)清除,這里getchar();只是取得了’\n’但是并沒(méi)有賦給任何字符變量,所以不會(huì)有影響,相當(dāng)于清除了這個(gè)字符.
作用1:從緩沖區(qū)讀走一個(gè)字符,相當(dāng)于清除緩沖區(qū)。
作用2:前面的scanf()在讀取輸入時(shí)會(huì)在緩沖區(qū)中留下一個(gè)字符’\n’(輸入完按回車鍵所致),所以如果不在此加一個(gè)getchar()把這個(gè)回車符取走的話,接下來(lái)的scanf()就不會(huì)等待從鍵盤鍵入字符,而是會(huì)直接取走這個(gè)“無(wú)用的”回車符,從而導(dǎo)致讀取有誤。
2.使用getchar()清理回車\n
這個(gè)問(wèn)題轉(zhuǎn)載自n_s_X14,但是作者在文章最后留了一個(gè)問(wèn)題,現(xiàn)在在這里給大家解釋一下原因。
文章的源碼為:
#include <stdio.h>
int main(void){
char m[40];
char n;
printf("please input first str:\n"); //提示用戶輸入第一個(gè)字符串
scanf("%s",m); //獲取用戶第一個(gè)輸入字符串
printf("you input str is :%s\n",m); //輸出用戶的輸入的第一個(gè)字符串
printf("input second char :\n"); //提示用戶輸入第二個(gè)字符
scanf("%c",&n); //獲取用戶的第二個(gè)字符
printf("now you input second char is :%c\n",n);//輸出用戶輸入的第二個(gè)字符
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
問(wèn)題:我們第一次輸入abc后成功打印出來(lái)了you input str is :abc,但是執(zhí)行到printf("input second char :\n");時(shí),還沒(méi)等到第二次輸入就打印出來(lái)了。這是為什么??
原因:
其實(shí)在我們第一次輸入并按下回車的時(shí)候,控制臺(tái)一共獲得了四個(gè)字符,分別是:a、b、c、回車(enter)。但是因?yàn)閟canf()方法遇到非字符的時(shí)候會(huì)結(jié)束從控制臺(tái)的獲取,所以在輸入’abc’后,按下 ‘回車(enter)’ 的同時(shí),將’abc’這個(gè)值以字符串的形式賦值給了類型為 ‘char’ 的 ‘m’ 數(shù)組,將使用過(guò)后的字符串: ‘回車(enter)’ 保存在控制臺(tái)輸入的緩沖區(qū),然后繼續(xù)執(zhí)行下一段輸出代碼,然后又要求用戶輸入。此時(shí),因?yàn)樯弦淮伪皇褂眠^(guò)后的字符串被保存在緩沖區(qū),現(xiàn)在scanf()方法從控制臺(tái)的緩沖區(qū)獲取上一次被使用過(guò)后的字符串,并只截取第一個(gè)字符: ‘回車(enter)’ ,此時(shí)控制臺(tái)緩沖區(qū)才算使用完了。所以在看似被跳過(guò)的輸入,其實(shí)已經(jīng)scanf()方法已經(jīng)獲取了我們的輸入了,這個(gè)輸入就是一個(gè) ‘回車(enter)’ 。
解決問(wèn)題:
使用getchar()方法,清除掉abc后面的緩存(回車enter)。
#include <stdio.h>
int main(void){
char m[40];
char n;
printf("please input first str:\n"); //提示用戶輸入第一個(gè)字符串
scanf("%s",m); //獲取用戶第一個(gè)輸入字符串
printf("you input str is :%s\n",m); //輸出用戶的輸入的第一個(gè)字符串
getchar();
printf("input second char :\n"); //提示用戶輸入第二個(gè)字符
scanf("%c",&n); //獲取用戶的第二個(gè)字符
printf("now you input second char is :%c\n",n);//輸出用戶輸入的第二個(gè)字符
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()清理緩存
文章結(jié)束時(shí)留了一個(gè)問(wèn)題:如果在第一次輸入ab后加一個(gè)空格再回車,又會(huì)出現(xiàn)原來(lái)的問(wèn)題,即程序只輸出了ab后就自動(dòng)跳過(guò)下一次的輸入之間退出了,控制臺(tái)輸出如下圖所示。

原因:
在獲取用戶第一個(gè)輸入字符串時(shí),scanf("%s",&m);,我們用%s作為轉(zhuǎn)換說(shuō)明,%s的作用是“把輸入解釋成字符串。從第一個(gè)非空白字符開(kāi)始,到下一個(gè)空白字符之前的所有字符都是輸入。”所以scanf把輸入的ab空格+回車就理解為ab+回車(ab后面沒(méi)有空格),但是依然以ab空格+回車的形式存儲(chǔ)在緩存區(qū)。我們輸入ab空格+回車,在緩存區(qū)是這樣存放的:

其中,第三格存放的為空格鍵。
當(dāng)程序運(yùn)行完 getchar();后,只清除了第三格中的空格鍵,因?yàn)橐淮螆?zhí)行getchar();只清除一個(gè)緩存,留下了第四格中的回車鍵,因此再次出現(xiàn)了同樣的問(wèn)題。
解決問(wèn)題:那么就是說(shuō)只要運(yùn)行兩次getchar();,清除掉第三格和第四格就可以正常了。
#include <stdio.h>
int main(void){
char m[40];
char n;
printf("please input first str:\n"); //提示用戶輸入第一個(gè)字符串
scanf("%s",m); //獲取用戶第一個(gè)輸入字符串
printf("you input str is :%s\n",m); //輸出用戶的輸入的第一個(gè)字符串
getchar(); //第一次清除緩存
getchar(); //第二次清除緩存
printf("input second char :\n"); //提示用戶輸入第二個(gè)字符
scanf("%c",&n); //獲取用戶的第二個(gè)字符
printf("now you input second char is :%c\n",n);//輸出用戶輸入的第二個(gè)字符
return 0;
}
Output:

由此可見(jiàn),當(dāng)我們第一次輸入ab空格+回車后,程序正常運(yùn)行。
進(jìn)一步:如果我們輸入的是a空格b+回車,scanf("%s",m); 這一步只能讀取到a,因?yàn)閍后面有空格。但是a空格b+回車在緩沖區(qū)這樣存放:

因此,如果想要程序正常運(yùn)行則需要在輸出用戶的輸入的第一個(gè)字符串后加入三次getchar();操作,即刪除掉第二,第三,第四格的內(nèi)容。
問(wèn)題:如果我們輸入a空格bbbbbbbb+回車,那可能需要無(wú)數(shù)個(gè)getchar();來(lái)清除緩存,這時(shí)應(yīng)該怎么辦??
解決方法:加入while循環(huán)while(getchar()!='\n') continue;
#include <stdio.h>
int main(void){
char m[40];
char n;
printf("please input first str:\n"); //提示用戶輸入第一個(gè)字符串
scanf("%s",m); //獲取用戶第一個(gè)輸入字符串
printf("you input str is :%s\n",m); //輸出用戶的輸入的第一個(gè)字符串
while(getchar()!='\n') //通過(guò)while循環(huán)刪除緩存
continue;
printf("input second char :\n"); //提示用戶輸入第二個(gè)字符
scanf("%c",&n); //獲取用戶的第二個(gè)字符
printf("now you input second char is :%c\n",n);//輸出用戶輸入的第二個(gè)字符
return 0;
}
這時(shí),我們輸入a空格bbbbbbbb+回車,程序正常運(yùn)行。

解析:
while(getchar()!='\n')
continue;
可以看出這段代碼代替了無(wú)數(shù)個(gè)getchar(),他的作用是跳過(guò)剩余的輸入行

第一次while循環(huán)消除第二格緩存,第二次while循環(huán)消除第三格緩存……直到第八次。最后一次同樣,getchar()也消除了回車。
while(getchar()!='\n')可以拆分成兩步,
第一步調(diào)用getchar()方法(這里getchar();只是取得了’\n’但是并沒(méi)有賦給任何字符變量,所以不會(huì)有影響,相當(dāng)于清除了這個(gè)字符)。
第二步判斷獲取到的緩存是否等于’\n’。
4.混合scanf()與getchar()
假設(shè)程序要求用getchar()處理字符輸入,用scanf()處理數(shù)值輸入,這兩個(gè)函數(shù)都能很好的完成任務(wù),但是不能混合使用。 因?yàn)間etchar()讀取每個(gè)字符,包括空格、制表符和換行符;而scanf()在讀取數(shù)字時(shí)則會(huì)跳過(guò)空格、制表符和換行符。
例:
要求用戶輸入一個(gè)字母和兩個(gè)數(shù)字,輸出以第一個(gè)數(shù)字為行數(shù),第二個(gè)數(shù)字為列數(shù),以字母為內(nèi)容的數(shù)列,要求可以不斷輸入直至鍵入回車退出程序:
#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:

我們發(fā)現(xiàn),在第一次輸入成功打印后,程序自動(dòng)退出。這明顯不符合我們的題目要求。
原因是,輸入的c23其實(shí)是c23+換行符,scanf()函數(shù)把這個(gè)換行符留在了緩存中。getchar()不會(huì)跳過(guò)換行符,所以在進(jìn)入下一輪迭代時(shí),還沒(méi)來(lái)得及輸入字符,它就讀取了換行符,然后將其賦值給了ch。而ch是換行符正式終止循環(huán)的條件。
如何改進(jìn)??
- 我們需要?jiǎng)h除scanf()函數(shù)留在緩存中的換行符即可。
- 在if語(yǔ)句中使用一個(gè)break語(yǔ)句,可以在scanf()的返回值不等于2時(shí)終止程序,即如果一個(gè)或兩個(gè)輸入值不是整數(shù)或者遇到文件結(jié)尾就終止程序。
#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()中轉(zhuǎn)化符的問(wèn)題


問(wèn)題:從上面兩張圖片中可以看出,當(dāng)scanf("%d",&c);改為scanf("%c",&c);時(shí),控制臺(tái)中出現(xiàn)了圖二的問(wèn)題。character為什么為空白??
原因:
如果格式是%c,那么任何字符都是它想要的,所以第二個(gè)程序中的第二個(gè)scanf("%c")會(huì)得到‘+’后面的空格’ '。如果格式是%d,則會(huì)忽略任何空白字符(空格、回車、制表符等),忽略的意思是,從緩沖區(qū)里刪除,但并不保存;如果遇到數(shù)字,則拿出并保存給后面的整數(shù),也就是說(shuō)%d的時(shí)候,scanf想要的字符是數(shù)字和空白符。所以第一個(gè)程序里的第二個(gè)scanf("%d")忽略掉了空格,正確輸入了數(shù)字。
總結(jié)
到此這篇關(guān)于C語(yǔ)言中g(shù)etchar()的原理以及易錯(cuò)點(diǎn)解析的文章就介紹到這了,更多相關(guān)C語(yǔ)言 getchar()原理及易錯(cuò)點(diǎn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
深入了解C語(yǔ)言中的動(dòng)態(tài)內(nèi)存分配
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言中的動(dòng)態(tài)內(nèi)存分配,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C語(yǔ)言有一定的幫助,需要的可以參考一下2022-06-06
利用C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)易版掃雷
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)易版掃雷,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-02-02
C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)的時(shí)間復(fù)雜度和空間復(fù)雜度
算法在編寫成可執(zhí)行程序后,運(yùn)行時(shí)需要耗費(fèi)時(shí)間資源和空間(內(nèi)存)資源 。因此衡量一個(gè)算法的好壞,一般是從時(shí)間和空間兩個(gè)維度來(lái)衡量的,即時(shí)間復(fù)雜度和空間復(fù)雜度,感興趣的同學(xué)可以參考閱讀2023-04-04
FFmpeg獲取網(wǎng)絡(luò)攝像頭數(shù)據(jù)解碼
這篇文章主要為大家詳細(xì)介紹了FFmpeg獲取網(wǎng)絡(luò)攝像頭數(shù)據(jù)解碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-06-06
C語(yǔ)言源碼實(shí)現(xiàn)停車場(chǎng)管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言源碼實(shí)現(xiàn)停車場(chǎng)管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12
使用C++實(shí)現(xiàn)鏈表元素的反轉(zhuǎn)
反轉(zhuǎn)鏈表是鏈表操作中一個(gè)經(jīng)典的問(wèn)題,也是面試中常見(jiàn)的考題,本文將從思路到實(shí)現(xiàn)一步步地講解如何實(shí)現(xiàn)鏈表的反轉(zhuǎn),幫助初學(xué)者理解這一操作,我們將使用C++代碼演示具體實(shí)現(xiàn),同時(shí)分析時(shí)間復(fù)雜度和空間復(fù)雜度,需要的朋友可以參考下2025-02-02

