首頁 > 軟體

C語言中getchar()的原理以及易錯點解析

2022-03-03 16:01:55

一.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 :%sn",m);    //輸出使用者的輸入的第一個字串
    printf("input second char :n");        //提示使用者輸入第二個字元
    scanf("%c",&n);                         //獲取使用者的第二個字元
    printf("now you input second char is :%cn",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 :%sn",m);    //輸出使用者的輸入的第一個字串
    getchar();
    printf("input second char :n");        //提示使用者輸入第二個字元
    scanf("%c",&n);                         //獲取使用者的第二個字元
    printf("now you input second char is :%cn",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 :%sn",m);    //輸出使用者的輸入的第一個字串
    
    getchar();                              //第一次清除快取
    getchar(); 								//第二次清除快取
    
    printf("input second char :n");        //提示使用者輸入第二個字元
    scanf("%c",&n);                         //獲取使用者的第二個字元
    printf("now you input second char is :%cn",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 :%sn",m);    //輸出使用者的輸入的第一個字串
    
    while(getchar()!='n')					//通過while迴圈刪除快取
        continue;
        
    printf("input second char :n");        //提示使用者輸入第二個字元
    scanf("%c",&n);                         //獲取使用者的第二個字元
    printf("now you input second char is :%cn",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是換行符正式終止迴圈的條件。

如何改進??

  1. 我們需要刪除scanf()函數留在快取中的換行符即可。
  2. 在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()原理及易錯點內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com