天天看點

getchar、scanf以及緩沖區的概念

     1、getchar()是stdio.h中的庫函數,它的作用是從stdin流中讀入一個字元,也就是說,如果stdin有資料的話不用輸入它就可以直接讀取了。

      getch()和getche()是conio.h中的庫函數,它的作用是從鍵盤接收字元,getchar帶有回顯。

      與前面兩個函數的差別在于: getchar()函數等待輸入直到按回車才結束(前提是緩沖區沒有資料),回車前的所有輸入字元都會逐個顯示在螢幕上。但隻有第一個字元作為函數的傳回值。

#include "stdio.h"
#include "stdlib.h"
int main(void)
{
	char c;
	while((c=getchar())!='\n')     //每個getchar()依次讀入一個字元
		printf("%c",c);            //按照原樣輸出
	printf("\n");
	system("pause");
	return 0;
}           

       程式運作時,首先停下來,等你輸入一串字元串,輸入完畢後,它把你輸入的整個字元串都輸出來了,咦,你不是說getchar()隻傳回第一個字元麼,這裡怎麼?

      因為我們輸入的字元串并不是取了第一個字元就把剩下的字元串丢掉了,它還在我們的記憶體中,好比,開閘放水,我們把水放到閘裡去以後,開一次閘就放掉一點,開一次就放掉一點,直到放光了為止,這裡開閘動作就相當于調用一次getchar()。我們輸入的字元串也是這麼一回事,首先我們輸入的字元串是放在記憶體的緩沖區中的,我們調用一次getchar()就把緩沖區中裡出口最近的一個字元輸出,也就是最前面的一個字元輸出,輸出後,就把它釋放掉了,但後面還有字元串,是以我們就用循環把最前面的一個字元一個個的在記憶體中釋放掉,直到滿足循環條件退出為止。

     例子中循環條件裡的'\n '實際上就是你輸入字元串後的回車符,是以意思就是說,直到遇到回車符才結束循環,而getchar()函數就是等待輸入(或緩沖區中的資料)直到按回車才結束,是以實作了整個字元串的輸出。當然,我們也可以把循環條件改一下,比如while ((c=getchar())!='a'),什麼意思呢,意思就是遇到字元'a'就停止循環,當然意思是如果你輸入“12345a213123 ”那麼隻會輸出到a前面的那個字元,結果是12345。

     再次注意:用getchar()它是從“流”中間去讀取,是以第一個getchar()接受的是剛剛中斷的流隊列中即将出列的第一個字元(不限于回車符,上面舉過例子了),如果流隊列不為空,執行getchar()就繼續放水,直到把回車符也放空為止,空了之後再在執行getchar()就停下等待你的輸入了;我們用getch()為什麼每次都是等待使用者的輸入呢?因為getch()是從鍵盤接收,即時的接收,并不是從stdin流中去讀取資料。

     下面是我的讨論:

     先來一段code:

#include "stdio.h"
#include "stdlib.h"
int main(void)
{
	char c;
	for(;(c=getchar())!='a';)
		printf("%c",c);
	getchar();
	c=getchar();
	printf("%c",c);
	system("pause");
	return 0;
}           

輸入:  ssss回車

得到:   ssss

           光标處(等待輸入)

說明:由于一直沒有輸入字元a,是以此時程式沒有結束,進入到for循環後一直沒有退出。鍵入回車後,運作c=getchar(),依次從緩沖區内取出(for循環):'s''s''s''s'' ' 包括回車的換行符,并将其全部列印了出來。。

如果我們輸入:ssssa回車

得到:ssss光标處(等待輸入)

說明:程式讀入到字元a的時候跳出了for循環,但是由于我們用getchar();清除了換行符'\n ',後面第7句c=getchar();需要你輸入一個字元(因為ssssa後面并沒有新的字元),是以程式仍然沒有結束。如果我們注釋掉getchar();這一句,那麼c=getchar();這行代碼就可以讀取ssssa後面的回車符号了,就可以得到這樣的輸出:  

       ssss

       光标處(程式結束)

這個輸入ssssa中的回車中的換行符'\n '就被c=getchar();這一句讀取并輸出了。

        總結:

       鍵盤輸入的字元都存到緩沖區内,一旦鍵入回車,getchar就進入緩沖區讀取字元,一次隻傳回第一個字元作為getchar函數的值,如果有循環或足夠多的getchar語句,就會依次讀出緩沖區内的所有字元直到'\n '。要了解這一點,之是以你輸入的一系列字元被依次讀出來,是因為循環的作用使得反複利用getchar在緩沖區裡讀取字元,而不是getchar可以讀取多個字元,事實上getchar每次隻能讀取一個字元。如果需要取消' \n'的影響,可以用getchar();來清除,這裡getchar();隻是取得了'\n '但是并沒有賦給任何字元變量,是以不會有影響,相當于清除了這個字元,還要注意的是這裡你在鍵盤上輸入ssss看到的回顯正是來自于getchar的作用,如果用getch就看不到你輸入了什麼。

          2、scanf

      scanf這個庫函數比較奇怪,而且存在一定的缺陷,是以很多人都不用了,這裡還是要簡單介紹一下。

      scanf輸入字元串、整型、實型等資料判斷的方式都一樣,回車、空格、tab鍵都認為是一個資料的結束,當然字元的話,一個字元就是結束了。回車、空格等都有對應的ASCII碼,是以用scanf輸入字元時要小心這些東西被當成字元輸進去,而輸入字元串和整型、實型等資料時這些都被當成分隔符而不會被輸入到字元數組或變量裡。當然如果輸入格式不是"%s%s"而是"%s,%s"分隔符就是逗号了。

     說了這麼多舉幾個例子:

#include "stdio.h"
int main(void)
{
	char n1[10];
	char n2[10];
	scanf("%s",n1);
	scanf("%s",n2);
	printf("n1=%s,n2=%s",n1,n2);
	return 0;
}           

輸入:

hello回車

world回車

得到如下的輸出:

n1=hello,n2=wolrd光标處(程式結束)

       這裡hello後面就是輸入再多個回車、空格也不會被指派到n2中的,因為使用scanf函數輸入字元串的時候他們隻是被當做分隔符。另外就是輸入n2的時候,n2後面的那個回車也被當做了分隔符,是以輸出的時候,隻是簡單的輸出了n1和n2的内容,而沒有輸出回車換行符。

如果輸入:

hello回車

光标處(等待輸入)

說明回車被認成分隔符,是以程式還要你輸入一個字元串來賦給n2。

其實這時緩沖區裡是有一個'\n '被留下來的,程式改成這樣:

#include "stdio.h"
int main(void) 
{
	char n1[10]; 
	char n2[10];
	char n3,n4; 
	scanf("%s",n1); 
	scanf("%s",n2); 
	printf("n1=%s,n2=%s",n1,n2);
	n3=getchar();      //n3讀取了n2後面的那個回車字元并輸出
	printf("%c",n3);
	//n4=getchar();
	//printf("%c",n4); 
	return 0;
}           

輸入:

hello回車

world回車

得到:  

n1=hello,n2=wolrd

光标處(程式結束)

如果取消最後兩行的注釋,同樣的輸入得到:

 n1=hello,n2=wolrd

光标處(等待輸入)

說明此時緩沖區内隻有一個'\n ',第二個getchar就需要你再輸入一個字元了,緩沖區内已經沒有字元了。

   scanf不會把回車、空格賦給字元串但是會賦給字元,就如同getchar一樣,這時就要考慮'\n '的存在了。

比如:

#include "stdio.h"
int main(void) 
{
	char n1[10];
	char n2;
	scanf("%s",n1);
	scanf("%c",&n2);
	printf("n1=%s,n2=%d",n1,n2);
	return 0;
}           

輸入:

hello回車

得到:

n1=hello,n2=10光标處(程式結束)    //10是'\n '的ASCII碼.

如果輸入:

hello 空格回車(一定要有回車,因為scanf也是要等回車,準确說是' \n'才會去讀緩沖區的。)

得到:

n1=hello,n2=32光标處(程式結束)    //32是空格的ASCII碼。

再羅嗦一下,如果最後一句輸出n2=%d改成n2=%c,則輸入:

hello回車

得到:

n1=hello,n2=

光标處(程式結束)

是不是和getchar一樣可以把'\n '讀出來呢,呵呵。

總結一下就是:

如果scanf輸入的不是字元,那麼分隔符為回車,空格、tab鍵時,兩個資料之間的分隔符隻是起差別兩個資料的作用,把分隔好的兩個資料分别指派到各自定義好的變量或數組中去,兩個資料之間的分隔符被從緩沖區讀出但是不起任何作用,當然最後一個'\n '會被留在緩沖區内,除非用getchar();或scanf("%c",&c);把它讀出來。

回車是一定要有的,不管getchar還是scanf隻要是通過緩沖區輸入資料的函數都是等待Enter鍵'\n '出現才進入緩沖區的。再來個整型資料、字元串、字元的混合例子:

#include "stdio.h"
int main(void) 
{
	int a,b,c;
	char n1[10];
	char n2,n3;
	scanf("%d%d",&a,&b);
	scanf("%c",&n2);
	scanf("%d",&c);
	scanf("%s",n1);
	scanf("%c",&n3);
	printf("a=%d,b=%d,n2=%c,c=%d,n1=%s,n3=%c",a,b,n2,c,n1,n3);
	return 0;
}           

輸入:

12(若幹空格或回車都不影響結果,這裡用了回車)

34(這裡還要求輸入,因為scanf隻得到了一個整型資料,而緩沖區内沒有整型資料了。要有回車或空格表示這個資料結束了,留下來的空格或回車被下個%c接受,這裡用了回車,可以試一下空格)

45 jfdkjfa(回車)

得到:

a=12,b=34,n2=

,c=45,n1=jfdkjfa,n3=

光标處(程式結束)

       這裡說明一下過程:在前兩個整型資料輸入時,兩個資料之間無論是回車還是若幹空格都被scanf當做分隔符,好了,scanf讀到分隔符(回車或空格)時,把第一個整型資料送到變量a中,緩沖區中留下分隔符和下面的整型資料,這時scanf再讀當然先讀分隔符,但是要求輸入的還是整型資料(%d),是以分隔符被忽略,如果這時要求輸入字元%c(不是字元串%s),那麼分隔符将以一個位元組的形式送到字元變量裡,就如同這裡的n2。同理可以知道c和n1的儲存過程,最後的n3正是接收了輸入時的最後一個回車。

好了如果看到這裡你都了解了那麼看最後一個例子:

#include "stdio.h"
int main(void)
{
	int a;
	char ch;
	scanf("%d",&a);
	ch=getchar();
	printf("%d,%c",a,ch);
	return 0;
}           

輸入:

95回車

得到:

95,

光标處(程式結束)

很明顯這是由于分隔符(回車)被getchar讀取并輸出了,如果加入一句:getchar();

#include "stdio.h"
int main(void)
{
	int a;
	char ch;
	scanf("%d",&a);
	getchar();
	ch=getchar();
	printf("%d,%c",a,ch);
	return 0;
}           

輸入:

95回車

c回車

得到:

95,c光标處(程式結束)