GetChar緩存機(jī)制深入剖析
與緩存區(qū)相關(guān)最常見(jiàn)的操作就是字符的輸入與輸出操作getchar,getc,getch,getche,gets系列函數(shù)。
第一個(gè)例子(與getchar有關(guān)):
#include<stdio.h>
int main()
{
int ch;
ch=getchar();
ch=getchar();
printf("%d\n",ch);
return 0;
}
代碼如上,當(dāng)輸入一個(gè)字符按下回車后程序沒(méi)有等待你二次輸入就結(jié)束了,而且無(wú)論輸入什么運(yùn)行結(jié)果均是10,是不是很奇怪(反正我第一次遇到時(shí)感覺(jué)是很奇怪),更奇怪的是當(dāng)你一次性輸入多個(gè)字符如abcd結(jié)果又正確的打印出了98,為什么呢?這就是緩沖區(qū)的原因。
解釋如下:getchar定義在stdio.h文件中,我們?cè)趕tdio.h中可以找到其相關(guān)的定義:
#define getchar() getc(stdin)//即getchar等同于調(diào)用getc(stdin)
我們又找到getc的定義
#define getc(_stream) (--(_stream)->_cnt >= 0 \
? 0xff & *(_stream)->_ptr++ : _filbuf(_stream))
將其展開(kāi)即得:
if(--(stdin)->_cnt>=0)
return 0xff&*(stdin)->ptr++;
else
return filbuf(stdin);
代碼譯如下stdin是標(biāo)準(zhǔn)輸入流,查看MSDN與stdio.h中可以看到定義如下:
Stdio.h中:
#define stdin (&_iob[0])
跟蹤即可得:
_CRTIMP extern FILE _iob[];
從上面代碼可得_iob是FILE結(jié)構(gòu)類型的,查看stdio.h中可以看到FILE結(jié)構(gòu)體定義如下:
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
從FILE結(jié)構(gòu)中我們可以得到了上面getc宏定義中使用的_cnt,_ptr成員,但這些都是次要的,我們應(yīng)該不難發(fā)現(xiàn)有這樣幾個(gè)成員_bufsize,_base分別對(duì)應(yīng)的是緩沖區(qū)大小,緩沖區(qū)基地址,從這里得到一個(gè)顯而意見(jiàn)的結(jié)論就是getchar函數(shù)使用了緩沖機(jī)制。(_cnt對(duì)應(yīng)的是緩沖區(qū)的輸入的字節(jié)數(shù)目,_ptr對(duì)應(yīng)的是讀指針的位置)
getc宏定義詳解
--(stdin)->_cnt>=0
此句判斷是否緩沖區(qū)內(nèi)有數(shù)據(jù),有的話就減一(表示又讀了一個(gè)),并讀取數(shù)據(jù)return 0xff&*(stdin)->ptr++,讀完成后,將讀指針向前移一個(gè)位置【重要】
好了,講了這么多都是鋪墊,現(xiàn)在回到正題為什么會(huì)出現(xiàn)上述結(jié)果:)
出現(xiàn)上述結(jié)果追根結(jié)底還是由于getchar函數(shù)使用了緩沖(看了上面的,我想大家也知道了,確實(shí)使用了緩沖),當(dāng)輸入一個(gè)字符按下回車后程序沒(méi)有等待你二次輸入就結(jié)束了,而且無(wú)論輸入什么運(yùn)行結(jié)果均是10,這是因?yàn)楫?dāng)用戶輸入了一個(gè)字符后,并按下回車后,緩沖中會(huì)存入用戶輸入的字符以及換行鍵的ASCII碼(10 ~)【略去的回車的ASCII碼13,可能是為了跨平臺(tái),在Linux下,回車后就是換行10,Windows下回車是先回車回到首行,再換行,即13 10】(執(zhí)行第一次getchar實(shí)際上是執(zhí)行g(shù)etc的else語(yǔ)句,填充緩存區(qū)后_cnt=2,_prt指向0位置,執(zhí)行完后_cnt=1,_prt指向1位置),第二次執(zhí)行g(shù)etchar時(shí),調(diào)用getc不會(huì)再執(zhí)行else語(yǔ)句,執(zhí)行的是if語(yǔ)句,故第二次不再等待用戶輸入了,直接執(zhí)行,執(zhí)行后cnt=0,prt指向位置2,并返回ptr指向位置1時(shí)的結(jié)果,即10,由于10為換行鍵,當(dāng)執(zhí)行遇到此時(shí),會(huì)在執(zhí)行完后清空緩存,ptr重新指向了位置0,cnt=0。
當(dāng)輸入abc的時(shí)候分析一樣,只不過(guò)掃行了第二個(gè)getchar后,cnt=2,ptr指向了位置2。
相關(guān)文章
gazebo里通過(guò)節(jié)點(diǎn)發(fā)布topic讓關(guān)節(jié)轉(zhuǎn)動(dòng)實(shí)現(xiàn)詳解
這篇文章主要介紹了gazebo里通過(guò)節(jié)點(diǎn)發(fā)布topic讓關(guān)節(jié)轉(zhuǎn)動(dòng)實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12用C語(yǔ)言實(shí)現(xiàn)單鏈表的各種操作(一)
本篇文章是對(duì)用C語(yǔ)言實(shí)現(xiàn)單鏈表的各種操作進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05C/C++ Zlib庫(kù)封裝MyZip壓縮類的詳細(xì)過(guò)程
在軟件開(kāi)發(fā)中,文件的壓縮和解壓縮是一項(xiàng)常見(jiàn)的任務(wù),而ZIP是一種被廣泛應(yīng)用的壓縮格式,本文將聚焦于一個(gè)簡(jiǎn)化的C++實(shí)現(xiàn),通過(guò)分析代碼,我們將深入了解其設(shè)計(jì)和實(shí)現(xiàn)細(xì)節(jié),感興趣的朋友一起看看吧2023-11-11C語(yǔ)言實(shí)現(xiàn)數(shù)獨(dú)游戲
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)數(shù)獨(dú)游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03C++ 學(xué)習(xí)筆記實(shí)戰(zhàn)寫一個(gè)簡(jiǎn)單的線程池示例
這篇文章主要為大家介紹了C++實(shí)現(xiàn)一個(gè)簡(jiǎn)單的線程池學(xué)習(xí)實(shí)戰(zhàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10C語(yǔ)言中的setlinebuf()、utmpname()、rewind函數(shù)使用
這篇文章主要介紹了C語(yǔ)言中的setlinebuf()、utmpname()、rewind函數(shù)使用,是C語(yǔ)言中操作文件的一些基本函數(shù),需要的朋友可以參考下2015-08-08C語(yǔ)言實(shí)現(xiàn)爆炸展開(kāi)的掃雷詳解
windows自帶的游戲《掃雷》是陪伴了無(wú)數(shù)人的經(jīng)典游戲,本文將利用C語(yǔ)言實(shí)現(xiàn)這一經(jīng)典的游戲,文中的示例代碼講解詳細(xì),感興趣的可以學(xué)習(xí)一下,這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)爆炸展開(kāi)的掃雷游戲2022-07-07C/C++程序開(kāi)發(fā)中實(shí)現(xiàn)信息隱藏的三種類型
這篇文章主要介紹了C/C++程序開(kāi)發(fā)中實(shí)現(xiàn)信息隱藏的三種類型的相關(guān)資料,需要的朋友可以參考下2016-02-02Linux下g++編譯與使用靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的方法
下面小編就為大家?guī)?lái)一篇Linux下g++編譯與使用靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05