詳解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()來清空緩衝區。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章