2011-07-11 42 views
18

所以我打算到K & r第二版做練習。在做一些練習之後感覺很自信我想我會檢查這些函數的實際實現。那時我的信心逃離了現場。我無法理解它。瞭解ç內置庫函數實現

比如我檢查getchar()

這裏是libio/stdio.h

extern int getchar (void); 

所以我跟着它通過它的原型,並得到這樣的:

__STDIO_INLINE int 
getchar (void) 
{ 
    return _IO_getc (stdin); 
} 

我再次遵循它在libio/getc.c

int 
_IO_getc (fp) 
    FILE *fp; 
{ 
    int result; 
    CHECK_FILE (fp, EOF); 
    _IO_acquire_lock (fp); 
    result = _IO_getc_unlocked (fp); 
    _IO_release_lock (fp); 
    return result; 
} 

而且我帶到另一個頭文件libio/libio.h,這是相當神祕:

#define _IO_getc_unlocked(_fp) \ 
     (_IO_BE ((_fp)->_IO_read_ptr >= (_fp)->_IO_read_end, 0) \ 
    ? __uflow (_fp) : *(unsigned char *) (_fp)->_IO_read_ptr++) 

這是我終於結束了我的旅程。

我的問題是相當廣泛的。這是什麼意思呢?通過查看代碼,我無法爲我的生活找出任何合乎邏輯的東西。看起來像一堆代碼將層層抽象出來。

更重要的是,當它真的從stdin

+3

它讀取字符時,它調用'__uflow()'。 –

回答

24

_IO_getc_unlocked是一個可以內聯宏。這個想法是,你可以從該流的字符,而不必調用一個函數,使得它有望快速足夠緊密的循環使用等

讓我們把它拆開一層在同一時間。首先,什麼是_IO_BE

/usr/include/libio.h:# define _IO_BE(expr, res) __builtin_expect ((expr), res) 

_IO_BE是一個提示編譯器,即expr通常評估爲res。當期望爲真時,它被用於構造代碼流以加快速度,但沒有其他語義效果。因此,我們可以擺脫的是,留給我們:

#define _IO_getc_unlocked(_fp) \ 
    (((_fp)->_IO_read_ptr >= (_fp)->_IO_read_end) \ 
    ? __uflow(_fp) : *(unsigned char *)(_fp)->_IO_read_ptr++)) 

讓我們變成一個內聯函數此爲清楚:

inline int _IO_getc_unlocked(FILE *fp) { 
    if (_fp->_IO_read_ptr >= _fp->_IO_read_end) 
    return __uflow(_fp); 
    else 
    return *(unsigned char *)(_fp->_IO_read_ptr++); 
} 

總之,我們有一個指針到緩衝區和指針到緩衝區的末尾。我們檢查指針是否在緩衝區之外;如果不是,我們增加它並返回舊值的任何字符。否則,我們調用__uflow來重新填充緩衝區並返回新讀取的字符。因此,這允許我們避免函數調用的開銷,直到我們實際需要執行IO來重新填充輸入緩衝區爲止。

請記住,標準庫函數可能會這樣複雜;他們也可以使用C語言的擴展(如__builtin_expect),這些擴展不是標準的,可能不適用於所有的編譯器。他們這樣做是因爲他們需要快速,因爲他們可以對他們使用的編譯器做出假設。一般來說,除非絕對必要,否則你自己的代碼不應該使用這樣的擴展,因爲它會使移植到其他平臺變得更加困難。

+0

你確定'unsigned char *'作爲返回類型是正確的嗎?它不應該是'int'嗎? –

+0

啊,可以。沒有仔細查看'__uflow'的返回類型是什麼。但無論如何,這只是一個例子。 – bdonlan

+0

謝謝,非常有幫助。我還有一個問題。char * _IO_read_ptr;'char * _IO_read_end;' 如何設置? – saint

1

之所以獲得字符有一個標準庫,你不應該需要知道這些功能的具體細節植入。在某些時候實現庫調用的代碼必須使用非標準系統調用,這些調用必須處理您可能不關心的問題。如果你正在學習c確保你能理解其他C程序除了STDLIB一旦你多一點提前看STDLIB,但它仍然不會做的意義很多,直到你明白系統調用參與。

0

getchar()的定義將該請求重新定義爲來自stdin的字符的特定請求。

_IO_getc()的定義執行完整性檢查以確保FILE *存在並且不是文件結束,然後鎖定流以防止其他線程破壞對_IO_getc_unlocked()的調用。

_IO_getc_unlocked()的宏定義只是檢查讀取指針是否位於或超過文件點的末尾,如果是則調用__uflow,否則返回讀指針處的字符。

這是所有stdlib實現的標準東西。你不應該看它。事實上,許多stdlib實現將使用匯編語言進行優化處理,這更加神祕。

2

我可以極力推薦P.J.Plauger的The Standard C Library。他提供了關於標準的背景並提供了每個功能的實現。這個實現比你在glibc或現代C編譯器中看到的更簡單,但仍然使用宏,比如你發佈的_IO_getc_unlocked()

該宏將從緩衝數據(可能是ungetc緩衝區)中拖出一個字符,或者從流(可能會讀取並緩衝多個字節)讀取該字符。

+1

+1用於推薦一本書。 – saint

4

從僞代碼到真正的代碼去,我們可以把它分解:

if (there is a character in the buffer) 
    return (that character) 
else 
    call a function to refill the buffer and return the first character 
end 

讓我們使用the ?: operator

#define getc(f) (is_there_buffered_stuff(f) ? *pointer++ : refill()) 

一位接近:

#define getc(f) (is_there_buffered_stuff(f) ? *f->pointer++ : refill(f)) 

現在我們差不多了。要確定是否有一些已經緩存,它使用的文件結構指針和緩衝區

_fp->_IO_read_ptr >= _fp->_IO_read_end ? 

這實際上測試相反的條件,以我的僞代碼中的讀指針「是緩衝區空」,如果所以,它調用__uflow(_fp) // "underflow",否則,它只是達到直接與指針的緩衝,獲取字符,然後遞增指針:

? __uflow (_fp) : *(unsigned char *) (_fp)->_IO_read_ptr++)