60

この (恐ろしい、ひどい、良くない、非常に悪い) コード構造を考えてみてください。

#define foo(x) // commented out debugging code

// Misformatted to not obscure the point
if (a)
foo(a);
bar(a);

このコードでは、2 つのコンパイラのプリプロセッサが異なる結果を生成するのを見てきました。

if (a)
bar(a);

if (a)
;
bar(a);

明らかに、これは移植可能なコード ベースにとっては悪いことです。

私の質問: プリプロセッサはこれに対して何をすべきですか? 最初にコメントを削除するか、最初にマクロを展開しますか?

4

6 に答える 6

36

残念ながら、元のANSI C 仕様では、セクション 4 のプリプロセッサ機能が明示的に除外されています (「この仕様では C 言語のみが説明されています。ライブラリまたはプリプロセッサについては規定されていません。」)。

ただし、 C99 仕様はこの明示性を処理します。コメントは、前処理ディレクティブの解析の前に発生する「翻訳フェーズ」で単一のスペースに置き換えられます。(詳細はセクション 6.10)。

VC++GNU C コンパイラはどちらもこのパラダイムに従います。他のコンパイラは古い場合は準拠していない可能性がありますが、C99 に準拠している場合は安全です。

于 2009-10-02T17:39:18.117 に答える
11

C99 標準の変換フェーズのこのコピー アンド ペーストの説明で説明されているように、コメントの削除 (単一の空白に置き換えられます) は変換フェーズ 3 で発生し、前処理ディレクティブは処理され、マクロはフェーズ 4 で展開されます。

C90 標準 (私はハード コピーしか持っていないので、コピー アンド ペーストはありません) では、これらの 2 つのフェーズは同じ順序で発生しますが、変換フェーズの説明は、C99 標準とはいくつかの詳細でわずかに異なります。前処理ディレクティブが処理され、マクロが展開される前に、コメントが削除され、単一の空白文字に置き換えられます。

繰り返しますが、C++ 標準では、これら 2 つのフェーズが同じ順序で発生します。

' //' コメントの処理方法に関して、C99 標準では次のように規定されています (6.4.9/2)。

文字定数、文字列リテラル、またはコメント内を除いて、文字は // 次の改行文字までのすべてのマルチバイト文字を含むコメントを導入しますが、次の改行文字は含みません。

そして、C++ 標準は (2.7) と言っています:

文字 // はコメントを開始し、次の改行文字で終了します。

したがって、最初の例は明らかにその翻訳者のエラーです-マクロが展開されたときに ' ;' の後の文字をfoo(a)保持する必要がありますfoo()-コメント文字はマクロの「コンテンツ」の一部であってはなりませんthe foo()

しかし、バグのあるトランスレーターに直面しているため、マクロ定義を次のように変更することをお勧めします。

#define foo(x) /* junk */

バグを回避します。

ただし(そして、ここでトピックからずれています...)、コメントが処理される前に行のスプライシング(改行の直前のバックスラッシュ)が発生するため、次のような厄介なコードに遭遇する可能性があります。

#define evil( x) printf( "hello "); // hi there, \
                 printf( "%s\n", x); // you!



int main( int argc, char** argv)
{
    evil( "bastard");

    return 0;
}

これは、誰が書いたとしても驚くかもしれません。

または、ボックス スタイルのコメントが好きな人 (もちろん私ではありません!) によって書かれた次のコードを試してください。

int main( int argc, char** argv)
{
                            //----------------/
    printf( "hello ");      // Hey, what the??/
    printf( "%s\n", "you"); // heck??         /
                            //----------------/
    return 0;
}

コンパイラがデフォルトでトライグラフを処理するかどうかに応じて(コンパイラは想定されていますが、トリグラフはそれらに遭遇するほぼすべての人を驚かせるため、一部のコンパイラはデフォルトでそれらをオフにすることを決定します)、必要な動作が得られる場合と得られない場合があります。もちろん、それがどんな振る舞いであっても。

于 2009-10-02T19:32:13.250 に答える
5

MSDNによると、マクロが展開される前処理段階の前に行われるトークン化段階で、コメントは単一のスペースに置き換えられます。

于 2009-10-02T17:35:58.293 に答える
4

//コメントをマクロに入れないでください。コメントを付ける必要がある場合は、/ **/を使用してください。さらに、マクロに誤りがあります。

#define foo(x) do { } while(0) /* junk */

このように、fooは常に安全に使用できます。例えば:

if (some condition)
    foo(x);

fooが何らかの式に定義されているかどうかに関係なく、コンパイラエラーをスローすることはありません。

于 2009-10-02T19:52:53.663 に答える
2
#ifdef _TEST_
#define _cerr cerr
#else
#define _cerr / ## / cerr
#endif
  • 一部のコンパイラ (VC++) で動作します。_TEST_が定義されていない場合、

    _cerr ...

    コメント行に置き換えられます

    // ええと...

于 2011-01-06T01:13:55.580 に答える
1

コンプライアンスには次の 3 つのステップが必要であることを思い出すようです。

  1. ストリップ
  2. マクロを展開する
  3. もう一度剥がす

この理由は、コンパイラが .i ファイルを直接受け入れることができることに関係しています。

于 2009-10-02T20:23:38.920 に答える