21

私はカーニハンのCプログラミング本を読んでいて、出くわしputcましfputcた。両者の違いや、いつ使用されるのかよくわかりませんでした。このトピックを扱ったStackOverflowの投稿をいくつか見つけましたが、それでも不明です。

ここで述べたように(putcにはstdoutが必要ですが、vs puts):

  1. Kernighanの本によると、マクロとputc同等ですfputcputc、マクロとして実装でき、putcそのストリーム引数を複数回評価する可能性があります。

  2. との違いはputcfputcを使用するputcと、ストリーム引数を複数回評価する必要がある可能性があるため、本質的に安全ではないマクロバージョンを実行するリスクがあることです。これは、ほとんどの人が気付いていないために気をつけていない合併症を引き起こすので、fputc使用することをお勧めします。fputcのマクロにはこの問題はありません。

質問:

  1. putcマクロとして実装できますが、同じことを行う際の問題は何fputcですか?

  2. 2番目のステートメントは、いくつかの合併症と安全性の問題について言及しています。それらは何ですか?

  3. putcその引数を複数回評価します。それで、議論を評価することと比較してそれがもたらす長所または短所は何ですか。

4

1 に答える 1

21

マクロ実装の問題は、引数のいずれかに副作用がある場合、それらの副作用が複数回評価され、未定義の動作につながる可能性があることです。このおもちゃの例を考えてみましょう。

#define SQUARE(x) ((x) * (x))

ほとんどの場合、これは期待どおりに機能しますが、などの式を渡すと、プリプロセッサはCを認識しない単なるテキストトランスフォーマーであるためf()、関数を呼び出すことの副作用は1ではなく2回f()発生します。

int f()
{
    printf("f() was called\n");
    return 42;
}
...
int x = SQUARE(f());  // This calls f() twice!  It gets expanded to this:
// int x = (f() * f());

これを概観するために、putc関数がマクロとして実装されている場合、そのstream引数を複数回評価する場合があります。したがって、そのストリームが関数からのものである場合:

FILE *get_file()
{
    // Potential side effects could happen here
    return some_file;
}
...
putc('A', get_file());

その結果、関数get_file()が複数回呼び出され、望ましくない副作用が発生する可能性があります。

fputc()もちろん、解決策は、の代わりにのような通常の関数を呼び出すことですputc()。マクロではないため、引数を複数回評価する際に問題が発生する可能性はありません。マクロは危険な場合があるため、注意して使用してください。

于 2012-12-23T05:54:22.387 に答える