web-dev-qa-db-ja.com

Cプリプロセッサは最初にコメントを削除するか、マクロを展開しますか?

次の(恐ろしい、恐ろしい、良くない、非常に悪い)コード構造を考えてみましょう:

#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);

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

私の質問:プリプロセッサはこれで何をすることになっていますか?最初にコメントを省略しますか、それともマクロを最初に展開しますか?

54
Phil Miller

残念ながら、元の ANSI C仕様 は、セクション4のプリプロセッサ機能を明確に除外しています(「この仕様は、C言語のみを説明しています。ライブラリもプリプロセッサも提供していません。」)。

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

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

31
Reed Copsey

C99標準の翻訳フェーズの this copy-n-pasted decription で説明されているように、前処理ディレクティブが処理され、マクロが実行されている間、コメントの削除(コメントは単一の空白で置き換えられます)が翻訳フェーズ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;
}
_

コンパイラーがデフォルトで処理するかどうかに応じて trigraphs かどうか(コンパイラーは想定されていますが、トライグラフはそれらを実行するほぼすべての人を驚かせるので、一部のコンパイラーはデフォルトでオフにすることを決定します)、あなたが望む振る舞いを取得しない-もちろん、どんな振る舞いでも。

10
Michael Burr

[〜#〜] msdn [〜#〜] によると、コメントはトークン化フェーズで単一のスペースに置き換えられます。これは、マクロが展開される前処理フェーズの前に発生します。

5
Jim Lewis

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

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

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

if (some condition)
    foo(x);

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

4
Vitali
#ifdef _TEST_
#define _cerr cerr
#else
#define _cerr / ## / cerr
#endif
  • 一部のコンパイラ(VC++)で動作します。いつ _TEST_ 定義されていません、

    _cerr ...

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

    // cerr ...

2
Mike Peeler

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

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

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

1
Joshua