2011-07-11 3 views
18

。いくつかのエクササイズをした後にかなり自信が持てると感じましたが、私はこれらの機能の実際の実装をチェックすると思っていました。それは私の自信だった。私はそれを理解できませんでした。私はgetchar()をチェック例えばの理解Cビルトインだから私は練習をやってK & R第二版を通過したライブラリ機能の実装

ここでは、だから私はそれを介してそれに従うと、これを取得します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はINLINABLEマクロあります。アイデアは、のは、一度に1つの層を、それを離れてみましょう

など、あなたはそれがうまくいけば十分に速くタイトなループ内で使用すること、関数を呼び出すことなく、ストリームから文字を得ることができるということです。まず、_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を学習している場合は、STDLIBでもう少し事前の外観を得れば、あなたがSTDLIB以外の他のCプログラムを理解できることを確認していますが、システムが関与呼び出しを理解するまでには、まだ感覚の多くをすることはありません。

0

GETCHARの定義は、()標準入力から文字の特定の要求としての要求を再定義します。

_IO_getc()の定義では、FILE *が存在し、ファイルの終わりではないことを確認するために、他のスレッドが_IO_getc_unlocked()の呼び出しを壊さないようにストリームをロックします。

_IO_getc_unlocked()のマクロ定義では、読み取りポインタがファイルポイントの終わりにあるかどうかを確認し、存在する場合は__uflowを呼び出すか、そうでない場合は読み取りポインタでcharを返します。

これは、すべての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++) 
関連する問題