街中で見かけて「グッときた人」の思い出

c言語のgetcのマクロについて

この下記のマクロは、入力ストリームより文字を読んでいますが、フアイルの終わりである場合のマクロの動作を教えていだきたい、
特に[-1]が (unsigned char)へ行く動作を教えて頂きたい。またマクロの説明書のwebサイトを教えて頂きたい。
よろしく願いします。

((--((f) - > blevel)>=0) ? (unsigned char)(++(f) -> fpi)[-1]:_fget(f))

A 回答 (4件)

「(unsigned char)(++(f) -> fpi)[-1]」についてですね。



まず、演算子の優先順位は「(f) -> fpi」が一番最初です。

たぶん「今、読み出すべき文字を指しているポインタ」が「fpi」なのでしょう。

次の優先順位は「++」ですから、fpiを「++」、つまり、インクリメントします。

インクリメントによって「fpiを次に読み出すべき場所」に更新しています。

次の優先順位は、要素参照の「[-1]」ですから、そのポインタに対して「ポインタから-1の位置にある要素を参照」しています。

つまり「さっきインクリメントする前に指してた場所の要素」を取り出してる訳です。

まとめると「今fpiが指してる場所からデータを1つ取り出して、fpiを1つ進めている」のです。

ぶっちゃけ
((--((f)->blevel)>=0)?(unsigned char)(*(f)->fpi++) : _fgetc(f))
と同じですが、判りにくいマクロですね。

重要なのは「[-1]」です。これは「値としての-1」ではなく「1つ前の場所」を意味します。

例えば
unsigned char c;
unsigned char buf[] = { "ABCDEFGHI" };
unsigned char *p;
p = &buf[5]; // pは「F」を指す
c = ++p[-1];
とした場合。

c = ++p[-1];
は「pを1つ進めGを指すようにしてから、pが指す場所から-1した場所にあるデータであるFを取り出してcに入れる」です。

つまり
c = ++p[-1];

c = *p++;
は「同じこと」をします。

で、なんで「こんな回りくどい書き方をするか」ですが、理由は「1行の中で、getcを複数使った場合」を考えての事だろうと思われます。

たぶん「式の評価順序による想定外の副作用」を防ぐ理由で「ほげほげ++」の使用を避け「++ほげほげ」を使いたかったのだと想像します。

しかし「式の評価順序による想定外の副作用」は「++ほげほげ」も「ほげほげ++」も「どっちも一緒」なので、マクロ作者の期待する「副作用の予防」にはなっていませんね。

蛇足ですが、最後の「_fget」は「_fgetc」の書き写し間違いですよね?
    • good
    • 0
この回答へのお礼

回答ありがとうございます。特に[-1]」について例を上げ,説明して頂きまして大変参考になりました。ただ、私が勉強不足のため優先順位の詳細がわからないので別途質問したいと思います。

お礼日時:2009/01/24 07:08

う~ん,


(++ほげ)[-1]
にしているのはもっと単純に
「++ほげ」の方が「ほげ++」より速いんじゃないかと思った
くらいだとおもうんだけど>#2.... この辺はアセンブリレベルまで落としてみないと見えない世界ですね.
「評価順序」って話をしたら, いずれにしても「2つの getc のうちどちらを先に評価するかわからない」時点でおしまい. ま, これはマクロバージョンでも関数バージョンでも同じことだけど.
    • good
    • 0

蛇足ですが。



「((--((f) - > blevel)>=0)」は「fpiが指すバッファの残りのバイト数を表わすblevelを1つ減らし、減らした結果がマイナスかどうか?」を判定します。

まだある(減らした後で0以上になった。0を含む)なら、fpiを1つ進めて、fpiが指す場所にあるデータを取り出し、unsigned charにキャストして返します。

もう無かった(減らした後でマイナスになった)なら、直接データを取り出せないので_fgetc(f)を呼びます。

呼ばれた_fgetc()関数は、バッファが空になっているので、開かれたファイルからデータを読み込み、バッファに格納します。

そして、fpiをバッファの先頭に再設定し、blevelも「バッファの残りバイト数」に再設定します。

最後に、バッファの先頭にあるデータを取り出し、fpiとblevelを1つ分更新し、取り出したデータを返り値にしてリターンします。

なお、バッファが空になっている時にファイルからデータを読もうとして「ファイルが尽きた」と判った場合は、ストリームに「EOFになった」とマークを付けた上で、EOFを返します。

ここまでが、呼ばれた_fgetc()関数の動作です。

>フアイルの終わりである場合のマクロの動作を教えていだきたい

そう言った訳で「ファイルが終わりになるのは、マクロの中ではなく、_fgetc()の中で起きる」のです。

マクロで「まだバッファにデータがある」と判断している間は_fgetc()は呼ばれないので「ファイルが終わりになる事はありえない」のです。

そして、ファイルが終わりになった場合は、blevelを幾らデクリメントしてもblevelはマイナスのままなので、常に_fgetc()が呼ばれます。そしてEOFは_fgetc()が返して来ます。

注意して欲しいのは「マクロ中にある[-1]はEOFではない」と言う事。

蛇足の蛇足ですが「_fgetc()が、EOF状態の時にblevelを固定しないで放置している」と言う場合、EOFになったストリームに対しマクロのgetcを使い続けると「blevelが1づつ引かれ続け、ある時、アンダーフローして0に戻り、呼ばれる筈の_fgetc()が呼ばれない」ので、非常に低い確率で誤動作します。

ま、普通、getcでEOFが返って来たら「ファイルを閉じて、それ以上getcしない」ので、問題は起きない筈なんですけどね。
    • good
    • 0
この回答へのお礼

回答ありがとうございます。特に、バッフア、_fgetc()関数の動作を説明して
頂きましてありがとうございます。

お礼日時:2009/01/24 07:17

入力ストリームのバッファにデータがある場合はその値をunsigned charとして読み込みバッファが空の場合は_fget( )関数を実行するのではないでしょうか。


ファイルの終わりは_fget( )関数の戻り値が帰るのかな。
FILE構造体や_fget( )の仕様が不明なので確かなことはわかりません。
    • good
    • 0
この回答へのお礼

回答ありがとうございます。

お礼日時:2009/01/24 07:19

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!


おすすめ情報