這兩天趕上高考,家裡有參加高考的,所以沒有來園子裡面逛逛, 今天高考完了,得閑了,所以出來透透氣。
上次我寫了些關於printf()函數的的文字, 感覺自己對輸入輸出不是很瞭解, 並且自己表述的也不是很完整,還幾處小毛病,因此想接著上次的話題繼續瞎掰。
那麼這次瞎掰點什麼呢 ? 那就從輸入輸出說起吧..................
1、流
我不知道為什麼國內要把stream 翻譯成流, 不過這個翻譯倒是挺形象的。 我們知道C語言是伴隨那個偉大的系統——Unix——而成長的,在Unix及其衍生的系統中,
系統資源都是當做檔案來看待的。C繼承了這個傳統,當我們從鍵盤輸入字元的時候,C會把輸入裝置(即鍵盤)當做檔案來處理。當我們利用C開發工具提供的輸入輸出函數
來進行輸入和輸出時,C會把輸入輸出的內容當做“流”來處理。
實際上流是一個理想話的對象,實際輸入或輸出裝置處理的內容映射到這個資料流中,而C會中這個資料流中擷取自己需要的資訊。
2、檔案
檔案: 不知道怎麼精確定義這個名詞, 如果照大部分資料來看,檔案是指儲存在儲存介質上的一堆0101資訊的集合,或者可以認為是儲存介質上一Block Storage地區。
前面說過在C中,系統資源當做檔案來處理, 這裡我們主要討論裝置檔案。
在C運行後,會預設自動開啟三個裝置檔案: 標準輸入檔案、標準輸出裝置和標準錯誤輸出檔案。
即: STDIN 標準輸入檔案 (stdin)
STDOUT 標準輸出檔案 (stdout)
STDERR 標準錯誤輸出檔案 (stderr)
在有些系統裡面應該是這樣定義的: #define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
有的系統是這樣定義的:
#define stdin (&_iob[0])
#define stdout (&_iob[1])
#define stderr (&_iob[2])
3、 緩衝型輸入系統和非緩衝型輸入系統
緩衝型輸入系統:是指當C要從從輸入檔案(這裡值標準輸入裝置,通常就是鍵盤)讀入資料流時,只有當使用者按下Enter ——(char)13 後使用者輸入的內容才能被被C使用。
非緩衝型的系統:是指當C從輸入檔案讀如資料流時, 不需要等待使用者按下Enter鍵,C就能立即使用。
如所示:
對於C而言,或者說對用C實現的執行實體而言,是否是緩衝或不緩衝,這個由系統決定,目前大部分系統對C而言是緩衝型輸入系統。
4、 getc()、putc()、getchar()、putchar()
需要說明的是這兩組函數應該是在效果上: getc()== getchar()
putc()==putchar()
但是他們的原型,或者說定義形式卻是不一樣的。
首先來看:getc()和getchar()
在C中getc()的函數原型:
Exp:
char getc(FILE *);
就是說如果要使用getch();函數你需要給他傳遞一個指標。
那麼getchar()是怎麼實現的呢? 這裡有兩種實現方式: 函數和宏; 我們就說說宏的定義:
Exp:
#define getchar() getc(stdin)
同樣來看:putc()和putchar(); 同樣putc()需要一個檔案指標,即其函數原型如:
Exp:
int putc(FILE *);
於是putchar()的宏定義就是:
Exp:
#define putchar() putc(stdout)
5、 EOF
在用getchar()時,我們會從標準輸入裝置一個一個字元的讀取所輸入的字元,包括空白字元,例如:空格 、定位字元 和 斷行符號;在進行判斷的時候可以用一個EOF
來判斷是否是輸入的結束,這樣可以輸入任何想輸入的字元。
Exp:
char chInPut;
while(EOF != (chInPut=getchar() ))
{
putchar(chInPut);
}
當我們輸入:a b c dd
就會有輸出:a b c dd
6、getc()
在VC 6.0中有兩個get()的定義, 一個是宏,一個是函數
宏的定義如下:
#define getc(_stream) (--(_stream)->_cnt >= 0 ? 0xff & *(_stream)->_ptr++ : _filbuf(_stream))
函數定義如下:
_CRTIMP int __cdecl getc(FILE *);
在C語言的各家編譯器提供廠商裡面有一個不成為的“潛規則”,那就是如果一個標識符前面如果是以底線開頭,這樣的標識符通常是編譯器預定義的宏,或者預定義的標誌符。
我們看宏定義;這裡用到的宏實際還用到了一個預定義的函數:
_CRTIMP int __cdecl _filbuf(FILE *)
從這個函數可以看出在getc()宏中使用的:_stream是一個具有檔案指標類型性質的預定義標識符。為了弄明白這個宏我們需要知道FILE類型的各個域;
其實FILE類型是另一個類型的重定義:
Exp :
struct _iobuf
{
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef _iobuf FILE;
從上面可知FILE類型的結構體具有一個_cnt的域;這樣我們就來解析getc()的宏;首先看:
--(_stream)->_cnt >= 0
在C語言的規範裡面,我們知道 -> 的預算優先順序與尾碼 --的一樣,同時高於首碼 -- 的優先順序, 因此這裡就是每次執行這個宏的時候,就是先判斷_stream->cnt - 1
後其值是否大於等於0;如果大於或等於0的話,那麼這個宏就返回:0xff & *(_stream)->_ptr++ ; 如果小於0的話,那麼這個宏就返回: _filbuf(_stream)當大於等於
0的時候就說明緩衝區內還有文字資料流字元,這時取出當前字元並與0xFF進行位與作為getc(_stream)的傳回值, 並且將字元指標後移一位。
當 _stream->cnt - 1的值小於0的時候就表示緩衝區內的字元已經全部遍曆過。但這時緩衝區內的內容還存在,只是指向緩衝區的指標已經移動到緩衝區後面,因此這
時就需要更新緩衝區內的內容,通過函數_filbuf(_stream)來實現, 函數_filbuf(FILE *) 同時將_stream的各個域重新賦值。
細心的朋友也許可以發現: #define stdin (&_iob[0]) 中的_iob[0];其實也是一個FILE類型的指標數組,其引用是通過: extern FILE _iob[]; 來實現的。
就像前面說的 va_list 一樣_stream 標識符由編譯器的供應商定義,並且由編譯器解釋。這裡我們就不在討論這個FILE類型的預定義標識符。
如果那位大俠需要自己嘗試做個C的編譯工具,可以研究研究。