1. 程式人生 > >關於緩衝區,如果c primer plus 第八章看不懂就先看這個!!!!!

關於緩衝區,如果c primer plus 第八章看不懂就先看這個!!!!!

c語言輸入輸出緩衝區的概念(一)(說的非常好,轉載自網上)
你肯定會奇怪為什麼一開始先說這個,一開始不都是資料型別什麼的嘛,這個寫在最前面因為後面的程式即使最簡單的code都會用到輸入輸出,輸出比較簡單,可以放在後面再說,但是輸入就不同了,如果不先了解一下,可能會得到和你預想不同的結果哦^_^.也正是由於和一般的c語言介紹方式不同,為了看起來正規一些,我就把這章叫做chapter0了,完全可以先跳過去,直接看chapter1.
1.getchar
先引用一下前人的成果(有修改)^_^:http://blog.csdn.net/cxyol/archive/2006/03/18/628324.aspx
getchar()是stdio.h中的庫函式,它的作用是從stdin流中讀入一個字元,也就是說,如果stdin有資料的話不用輸入它就可以直接讀取了。而getch()和getche()是conio.h中的庫函式,它的作用是從
鍵盤接收字元。getchar帶有顯示。
    與前面兩個函式的區別在於: getchar()函式等待輸入直到按回車才結束(前提是緩衝區沒有資料),回車前的所有輸入字元都會逐個顯示在螢幕上。但只有第一個字元作為函式的返回值。
#include<stdio.h>
#include<conio.h> /*我在gcc裡沒找到這個標頭檔案,可能改名或者取消了吧,以後找到再說.*/
void main()
{
    char c;
     c=getchar(); /*從鍵盤讀入字元直到回車結束*/
           //getchar()在這裡它只返回你輸入字串的第一個字元,並把返回值賦值給c
    putchar(c); /*顯示輸入的第一個字元*/
    printf("\n\n");
}
這個程式你執行一下,相信你又會有疑問了。這個就是從緩衝區中讀取了例子。第一次getchar()時,確實需要人工的輸入,但是如果你輸了多個字元,以後的getchar()再執行時就會直接從緩衝區中讀取了。
#include<stdio.h>#include<conio.h>void main(){    char c;    while ((c=getchar())!='\n') /*每個getchar()依次讀入一個字元*/        printf("%c",c); /*按照原樣輸出*/    printf("\n\n");}
程式執行時,首先停下來,等你輸入一串字串,輸入完畢後,它把你輸入的整個字串都輸出來了,咦,你不是說getchar()只返回第一個字元麼,這裡怎麼?
因為我們輸入的字串並不是取了第一個字元就把剩下的字串丟掉了,它還在我們的記憶體中,就好比,開閘放水,我們把水放到閘裡去以後,開一次閘就放掉一點,開一次就放掉一點,直到放光了為止,這裡開閘動作就相當於呼叫一次getchar()。我們輸入的字串也是這麼一回事,首先我們輸入的字串是放在記憶體的緩衝區中的,我們呼叫一次getchar()就把緩衝區中裡出口最近的一個字元輸出,也就是最前面的一個字元輸出,輸出後,就把它釋放掉了,但後面還有字串,所以我們就用迴圈把最前面的一個字元一個個的在記憶體中釋放掉,直到不滿足迴圈條件退出為止。
例子中迴圈條件裡的'\n'實際上就是你輸入字串後的回車符,所以意思就是說,直到遇到回車符才結束迴圈,而getchar()函式就是等待輸入(或緩衝區中的資料)直到按回車才結束,所以實現了整個字串的輸出。當然,我們也可以把迴圈條件改一下,比如while ((c=getchar())!='a'),什麼意思呢,意思就是遇到字元'a'就停止迴圈,當然意思是如果你輸入“213123\n”那麼只會輸出到a,結果是。

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

whieet補充:和上面對比。注意這裡是getch()!!!另外,大家執行時註釋掉putchar(c);這一行會有更清楚的理解

#include<stdio.h>
#include<conio.h> 
void main()
{
    char c;
c=getch(); //觀察一下啊。輸入後立即生效,不用按回車
putchar(c); //直接列印。
    printf("\n\n");
}

補充:按鍵盤上的回車產生了2個字元:回車符('\r')和換行符('\n')。回車符'\r'(CR:carriage return:倒車)使游標回到這行的首部,換行符('\n')(new line)然後再換行。
    所以當輸入字元'w',並按下回車鍵以後。首先得到回車符。那個getchar函式結束了。 但是還存在一個換行符。所以如果用getchar()來做判斷的時候。最好再寫一次getchar()清除緩衝區的'\n'.
3.如何清空輸入緩衝區的內容?
    如果我想讓getchar()每次都能夠等待使用者輸入的話就要清空緩衝區,下面就介紹方法(不同平臺)
       C標準規定 fflush()函式是用來重新整理輸出(stdout)快取的。對於輸入(stdin),它是沒有定義的。但是有些編譯器也定義了 fflush( stdin )的實現,比如微軟的VC。其它編譯器是否也定義了 fflush( stdin )的實現應當查詢它的手冊。GCC編譯器沒有定義它的實現,所以不能使用 fflush( stdin )來重新整理輸入快取。
       對於沒有定義 fflush( stdin )的編譯器,可以使用 fgets()函式來代替它(比用 getchar()、scanf()等函式通用性好)。可以這樣忽略輸入流中留下的回車等其它輸入,從而使下一次的輸入總保持一個“乾淨”的狀態。(這個是任何平臺下都可以的)
char sbuf[1024];
// ...fgets( sbuf, 1024, stdin );
// ...在windows 的vc下面就可以這樣了:for(int i=0;i<10;++i){       char ch=getchar();       fflush(stdin); //每次都會有等待狀態了}這裡說到gcc編譯器沒有定義fflush的實現,我們一般用getchar();來清除緩衝區.下面是我的討論:
先來一段code:#include <stdio.h>
main()
{
    char c;
    for(;(c=getchar())!='a';)
    printf("%c",c);
    getchar();
     c=getchar();
    printf("%c",c);
}輸入:
ssss回車
得到:
ssss
游標處(等待輸入)說明:此時程式沒有結束,進行到for迴圈,因為並沒有字元a出現,所以還沒跳出for迴圈.鍵入回車後,getchar
依次從緩衝區內取出(for迴圈):'s''s''s''s''\n'如果我們輸入:
ssssa回車
得到:
ssss游標處(等待輸入)說明:程式已經跳出for迴圈,但是由於我們用getchar();清除了換行'\n',後面第7句c=getchar();需要你輸入一個字元(因為ssssa後面並沒有新的字元),所以程式仍然沒有結束.如果我們註釋掉getchar();這一句,那麼得到:
ssss
游標處(程式結束)
這個輸入ssssa是的回車中的換行符'\n'就被c=getchar();這一句讀取並輸出了。
總結:
鍵盤輸入的字元都存到緩衝區內,一旦鍵入回車,getchar就進入緩衝區讀取字元,一次只返回第一個字元作為getchar函式的值,如果有迴圈或足夠多的getchar語句,就會依次讀出緩衝區內的所有字元直到'\n'.要理解這一點,之所以你輸入的一系列字元被依次讀出來,是因為迴圈的作用使得反覆利用getchar在緩衝區裡讀取字元,而不是getchar可以讀取多個字元,事實上getchar每次只能讀取一個字元.如果需要取消'\n'的影響,可以用getchar();來清除,這裡getchar();只是取得了'\n'但是並沒有賦給任何字元變數,所以不會有影響,相當於清除了這個字元.還要注意的是這裡你在鍵盤上輸入ssss看到的回顯正是來自於getchar的作用,如果用getch就看不到你輸入了什麼.再引一篇文章:http://www.cnblogs.com/biser/archive/2004/09/23/45704.aspx1.機理 你鍵盤輸入了東西,而此時你又沒有用程式去getchar她,請問這個時候你按的鍵的狀態儲存在何處?為什麼你一會兒去getchar的時候能得到呢 
(例子好舉,你先做一個1分鐘延遲,然後再getchar,會發現一分鐘前按的東西會顯示出來) 
實際上是 輸入裝置->記憶體緩衝區->程式getchar 
你按的鍵是放進緩衝區了,然後供程式getchar 
你有沒有試過按住很多鍵然後等一會兒會滴滴滴滴響,就是緩衝區滿了,你後頭按的鍵沒有存進緩衝區. 2.getchar()和getch() 然後就可以給你講了 
getchar是回車以後才進緩衝區 
getch是每次都進緩衝區 
用你的程式來說(我怎麼覺得應該是\n不是/n) 其實你輸入computer,沒按回車之前, 執行都停止在 
getchar()裡頭,根本沒有進入迴圈,自然也沒有執行printf 
當你一按回車,才從getchar出來,然後以後因為鍵盤緩衝區裡頭有東西,就一個一個字元getchar出來了 想立刻回顯,用getch就好了××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
c語言輸入輸出緩衝區的概念(二)(說的非常好,轉載自網上)
2.scanfscanf這個庫函式比較奇怪,而且存在一定的缺陷,所以很多人都不用了,這裡還是要簡單介紹一下.scanf輸入字串,整型,實型等資料判斷的方式都一樣,回車,空格,tab鍵都認為是一個數據的結束,當然字元的話,一個字元就是結束了,回車,空格等都有對應的ascii碼,所以用scanf輸入字元時要小心這些東東被當成字元輸進去,而輸入字串和整型,實型等資料時這些都被當成分隔符而不會被輸入到字元陣列或變數裡.當然如果輸入格式不是"%s%s"而是"%s,%s"分隔符就是逗號了,這個講到輸入輸出函式時再說.
說了這麼多舉幾個例子:#include <stdio.h>
int main()
{
char n1[10];
char n2[10];
scanf("%s",n1);
scanf("%s",n2);
printf("n1=%s,n2=%s",n1,n2);
}輸入:
hello回車
world回車
得到:
n1=hello,n2=wolrd游標處(程式結束)
這裡hello後面就是輸入再多個回車,空格也不會被賦值到n2中的,因為他們只是分隔符.如果輸入:
hello回車
游標處(等待輸入)
說明回車被認成分隔符,所以程式還要你輸入一個字串來賦給n2.其實這時緩衝區裡是有一個'\n'被留下來的,程式改成這樣:#include <stdio.h>
int main()
{
char n1[10];
char n2[10];
char n3,n4;
scanf("%s",n1);
scanf("%s",n2);
printf("n1=%s,n2=%s",n1,n2);
n3=getchar();
printf("%c",n3);
//n4=getchar();
//printf("%c",n4);
}輸入:
hello回車
world回車
得到:
hello
world
n1=hello,n2=wolrd
游標處(程式結束)
如果取消最後兩行的註釋,同樣的輸入得到:
hello
world
n1=hello,n2=wolrd
游標處(等待輸入)
說明此時緩衝區內只有一個'\n',第二個getchar就需要你再輸入一個字元了,緩衝區內已經沒有字元了.scanf不會把回車空格賦給字串但是會賦給字元,就如同getchar一樣,這時就要考慮'\n'的存在了.
比如:#include <stdio.h>
int main()
{
char n1[10];
char n2;
scanf("%s",n1);
scanf("%c",&n2);
printf("n1=%s,n2=%d",n1,n2);
}輸入:
hello回車
得到:
n1=hello,n2=10游標處(程式結束) //10是'\n'的ascii碼.
如果輸入:
hello 空格回車(一定要有回車,因為scanf也是要等回車,準確說是'\r'才會去讀緩衝區的.)
得到:
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只要是通過緩衝區輸入資料的函式都是等待回車鍵'\r'出現才進入緩衝區的.再來個整型資料,字串,字元的混合例子:#include <stdio.h>
int main()
{
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);
}輸入:
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>
main(){
int a;
char ch;
scanf("%d",&a);
ch=getchar();
printf("%d,%c",a,ch);
}
輸入:
95回車
得到:
95,
游標處(程式結束)
很明顯這是由於分隔符(回車)被getchar讀取並輸出了,如果加入一句:getchar();#include <stdio.h>
main(){
int a;
char ch;
scanf("%d",&a);
getchar();
ch=getchar();
printf("%d,%c",a,ch);
}
輸入:
95回車
c回車
得到: 95,c游標處(程式結束)