1. 程式人生 > >詳解scanf、gets、getchar和getch 使用及其原理。

詳解scanf、gets、getchar和getch 使用及其原理。

scanf、gets、getchar和getch 使用及其原理。

一、說在最前:回車及換行。

概念

在計算機還沒有出現之前,有一種叫做電傳打字機。在電傳打字機打字時,在每行後面加兩個表示結束的字元,分別叫做回車和換行。

回車:是告訴打字機把列印頭定位在左邊界,不捲動滾筒;符號 \r;十六進位制 0x0d

換行:是告訴打字機把紙張向下方移動一行,不改變左右位置;符號 \n;十六進位制 0x0a

區別

Unix系統裡,每行結尾只有"<換行>",即按下Enter後會產生 ‘\n’ ( linux與 unix系統基本一致)

Win系統裡,每行結尾是"<回車><換行>",即按下Enter

後會產生 ‘\r’ 和 ‘\n’

Mac系統裡,每行結尾是"<回車>",即按下Enter後會產生 ‘\r’
上述系統按下 Enter 後都會進行回車和換行操作,而產生的 ‘\r’ ‘\n’ 則會保留在 輸入緩衝區

二、緩衝區

一般情況下,由鍵盤輸入的字元並沒有直接送入程式,而是被儲存在一個緩衝區當中+。緩衝又分為兩種,行緩衝和完全緩衝。對於完全緩衝來說,緩衝區滿時被清空(內容被髮送到指定的目的地)。這種緩衝通常出現在檔案輸入中。對於行緩衝來說,遇到一個換行符,則清空緩衝區,鍵盤輸入是標準的行緩衝,因此,按下換行鍵(回車鍵)的時候才會清空(鍵盤緩衝區)。
鍵盤緩衝區的資料接下來會被傳送到 輸入緩衝區(stdin), 輸入函式scanf、gets、getchar都是從輸入緩衝區中獲取資料的。如果輸入資料過多或者輸入緩衝區還有很多資料,那麼下次呼叫輸入函式時,不會要求你輸入,而是直接從輸入緩衝區中讀取資料。


在這裡插入圖片描述

三、scanf、gets、getchar和getch 區別及一些原理。

1. scanf

scanf 指定了輸入的格式,並按照格式說明符解析輸入對應位置的資訊並存儲於可變引數列表中對應的指標所指位置。

從 scanf() 角度看輸入

假設scanf() 根據一個%d轉換說明讀取一個整數:scanf 函式每次從輸入緩衝區中讀取一個字元,跳過所有空白字元,直到遇到第一個非空白字元才開始讀取。因為要讀取整數,所以 scanf 希望發現一個數字字元或者是 ’ + ’ 或 ’ - ’ 。如果找到一個數字或者 ’ + ’ 或 ’ - ’ ,它便儲存該字元,並讀取下一個字元。scanf 不斷地讀取和儲存,直到遇到非數字字元。如果遇到一個非數字字元,它便認為讀到了整數的末尾。然後 scanf 把非數字字元放回到輸入緩衝區中。( 這就意味著程式在下次讀取時,首先讀到的是上一次讀取時丟棄的非數字字元

) 最後 scanf 計算已讀數字相應的數值,並將計算後的值放入指定的變數中。

如果使用%s轉換說明:scanf 會讀取除空白字元以外的所有字元。scanf 跳過空白開始讀取第1個非空白字元,並儲存直到再次遇到空白字元。這就意味著 scanf 根據%s 轉換說明讀取一個不包括空白字元的字串。當scanf 把字串放進指定陣列中時,它會在字元序列末尾加上’\0’,讓陣列內容成為一個C字串。
空白符(空白符:指空格符、製表符、回車符)

例子解析
	現在假設你的緩衝區裡有:abcd\n1234\n  (其中\n是回車符)  執行:scanf("%s",name);的時候
由於scanf是讀資料直到看見空白符(空白符:指空格符、製表符、回車符)就停止的輸入函式所以執行後,把abcd存到了name中。
緩衝區於是變成了 :\n1234

	接下來的執行就有問題了,如果遇到了:scanf("%d",&number);怎麼辦?因為遇到了回車符,它並不是一個數字。
所以scanf還有一個特性,就是忽略先導的空白符。不管是有幾百個回車也好,幾萬個空格也罷,
只要它們連續地出現在緩衝區的開頭,就統統忽略他們。然後再讀有意義的字元。於是1234被讀入number。

	回到剛剛,當緩衝區還是:\n1234\n的時候,如果遇到了:scanf("%c",&sex);應該怎麼辦呢?
你說,那好辦呀,不是說了忽略前導空白符嗎。
跳過回車讀'1'呀!想法是好的,可這隻針對你的程式這一種情況。如果我編寫的程式就是統計使用者輸入了多少個回車呢
所以對scanf來講跳過前導空白符有個例外,當引數是%c的時候,就把緩衝區的第一個字元返回回去,不管是什麼。

	這樣的設計就有個問題,scanf對不同的引數表現出來的特性不一樣。得承認,這是個缺陷,但不是說這樣不好。
這樣的設計至少把發現所有字元的機會交給了使用者,設計者這樣想:如果程式設計師使用了scanf("%c",..)
那他就有必要知道這函式能把回車符讀出來,至於程式設計師對回車符感不感興趣,那就看他了
不感興趣的話,程式設計師也一定知道該怎麼處理。回到你的程式裡。

	當執行scanf("%s",name)的時候,要求你從鍵盤輸入,於是你輸入了"abc",然後“回車”
緩衝區裡自然而然地是:abc\n ,scanf把abc拿走了,留下了\n
緩衝區裡現在就剩下\n於是,下一個scanf ("%c",&sex); 想當然地讀取了\n

scanf ()

scanf 按格式從輸入緩衝區內讀取資料 (即從輸入緩衝區中取出),讀取並丟棄先導的空白符,直到非空字元才開始儲存,當讀取 到空白符,也就是 空格符、製表符、回車符時,停止讀取並將空白符 放回 輸入緩衝區 ( 注意:%c例外,不會放棄先導空白符,而是直接讀取第一個字元 )。切記:鍵盤輸入最後按下的回車鍵 會以 ‘\n’ 的形式存放進輸入緩衝區,當scanf 以%c讀取時會讀入上一次鍵盤輸入末尾的 \n 導致讀取資料錯誤,所以用%c時要清空輸入緩衝區,以免讀取到垃圾資料。

注意:scanf("%d\n",&num); \n在scanf格式串中不表示等待換行符,而是讀取並放棄 (丟掉,不放回輸入緩衝區) 連續的空白字元。(事實上,scanf格式串中的任何空白字元都表示讀取並放棄空白字元。而且,諸如%d這樣的格式也會扔掉前邊的空白,因此你通常根本不需要在scanf格式串中加入顯式的空白。)

因此,"%d\n"中的\n會讓scanf讀到非空白字元為止,而它可能需要讀到下一行才能找到這個非空白字元。這種情況下,去掉\n僅僅使用"%d"即可。

2. gets

gets從標準輸入裝置讀字串函式,可以無限讀取,不會判斷上限,以回車結束讀取。所以如果輸入的字串超過100個,它也不會做檢測,此時就會發生溢位。若輸入未超過100個,則gets從輸入緩衝區中讀取資料 當讀取到 \n
時,將 ‘\n’ 轉換成 ‘\0’ 作為字串結束標誌。(\n並沒有被放回去,所以不會產生像 scanf 讀取完後留下 \n, 而導致下次讀取單個字元的錯誤。)

3. getchar

從輸入緩衝區中讀取一個字元,若緩衝區中無資料就會等待使用者輸入,直到輸入回車鍵,將資料輸入到輸入緩衝區。getchar可以用來清空緩衝區(防止讀入垃圾資料)。
可以寫成如下函式:

void clear_in(void)
{
	int c;
	while( (c=getchar() ) != '\n' && c != EOF);
}

getchar會一直讀取字元直到輸入緩衝區內沒有資料(返回一個EOF = End Of File 檔案結束),這樣輸入緩衝區就清空乾淨了。

4. getch

getch直接從鍵盤獲取鍵值,不等待使用者按回車,只要使用者按一個鍵,getch就立刻返回。
getch是非緩衝輸入函式,即不能用getch來接收緩衝區已存在的字元。
getch是無回顯的讀取 (不顯示讀取鍵值)。

注意:getch();並非標準C中的函式,不存在C語言中。所以在使用的時候要注意程式的可移植性。
1.所在標頭檔案是conio.h。而不是stdio.h。
2.在使用之前要呼叫 initscr(),結束時要呼叫 endwin()。否則會出現不輸入字元這個函式
也會返回的情況。
3.若使標頭檔案 getch.h ,也會出現不輸入字元這個函式直接返回的情況,可以使用多個getchar()來清空緩衝區。