4

マクロが無効になっているときに次のコードが出てきたときに、トレースを有効/無効にするマクロをいじっていました。

int main()
{

  ("Hello world");

}

このコードは有効で、目的の効果が得られました (マクロが無効になっていると何も起こりません) が、正確に何が起こっているのかわかりませんでした。コンパイラは括弧を「名前のない」メソッド宣言と見なしていますか?

より明確にするために、コードは次のとおりです。

 #ifdef TRACE

    #define trace printf("%s %d -> ",__FILE__, __LINE__);printf
 else

    #define trace
 #endif

int main()
{

  trace("Hello world");

}

前もって感謝します。

4

5 に答える 5

18

最初の例のように、関数名が欠落している場合、それは「括弧演算子」ではありません。これは、演算子とオペランドの間の関連付けを変更する式の単なる構文要素です。この場合、単に何もしません。あなたが持っているのはただの表現です

"Hello world";

typeの値に評価され、char *その値は無視されます。その式を冗長なペアで囲むことができます()

("Hello world");

これは何も変更しません。

まったく同じ方法で書くことができます

(5 + 3);

コードの途中で value8に評価される式を取得します。これはすぐに破棄されます。

通常、コンパイラは、副作用のない式ステートメントのコードを生成しません。実際、C 言語では、すべての式ステートメントの結果が破棄されるため、"意味のある" 式ステートメントは、副作用のある式ステートメントだけです。コンパイラは通常、効果のないステートメントを検出して破棄するのが得意です (警告が表示される場合もあります)。

警告は迷惑になる可能性があるため、次のような効果のない式ステートメントを記述します

"Hello world";

良い考えではないかもしれません。通常、コンパイラはへのキャストをvoid、この警告を生成しない要求として認識します

(void) "Hello world";

したがって、それに応じてマクロを再定義することを検討してください。

もちろん、上記の手法を使用する場合、マクロの引数として副作用のあるtraceものを指定する場合は、覚えておく必要があります。

trace("%d\n", i++);

次に、「無効」形式では、次のようになります

("%d\n", i++);

(コンマ演算子で 1 つの式に連結された 2 つの部分式)。この場合、インクリメントの副作用はi持続し、無効にはなりません。全体がプレーンと同等です

i++;

また、関数呼び出しを引数として使用する場合

trace(get_trace_name());

「無効」フォームは次のようになります

(get_trace_name());

get_trace_name()コンパイラは、への呼び出しを破棄する必要があることを認識できるほど賢くない可能性があります。したがって、マクロを使用するときは注意してください。もちろん、実際のトレースを無効にするときに副作用を保持することが意図されている場合を除き、副作用のある引数を避け、関数呼び出しのある引数を避けてください。

于 2010-04-13T14:14:27.337 に答える
3

それが機能するかどうかは、マクロに引数として何を渡すかによって異なります (AndreyT が言及した副作用の問題を参照してください)。この場合は良性です。ただし、マクロが処理され、 TRACE が定義されていない場合にテキストが挿入されないため、次の方法の方がおそらく安全です。

#ifdef TRACE
    #define trace printf("%s %d -> ",__FILE__, __LINE__);printf
#else
    #define trace( ... )
#endif

コンパイラが可変長マクロをサポートしていると仮定します。もしそうなら、おそらく次のほうがより良い定義になるでしょう:

#ifdef TRACE
    #define trace( fmt, ...) printf("%s %d -> " fmt, __FILE__, __LINE__, __VA_ARGS__ ) ;
#else
    #define trace( ... )
#endif

"%s %d -> "と の間にコンマがないのfmtは意図的であり、必須であることに注意してください。また、隣接する文字列リテラルの連結が発生するためには、fmt引数がリテラル文字列定数でなければならないことに注意してください。どのような種類の変数でもエラーが発生しますが、いずれの場合でも書式指定子に変数を使用することはお勧めできません。

于 2010-04-13T14:30:46.327 に答える
2
("Hello world");

文字列への定数ポインタを返す式です。この値は消費されません。

括弧には特定の役割がなく、省略できます。

"Hello world";
于 2010-04-13T14:15:09.420 に答える
0

コンパイラが C99 をサポートしている場合 (または、以前にこの機能を備えた gcc を使用している場合)、可変長マクロを使用できます。

#ifdef TRACE
#define trace(...) printf("%s %d -> ",__FILE__, __LINE__);printf(__VA_ARGS__)
#else
#define trace(...)
#endif

これにより、引数の副作用で発生する可能性のある問題を回避できます。厳密な C89 コンパイラを使用している場合は、副作用を回避する必要があります...

于 2010-04-13T14:45:06.877 に答える
0
#ifdef TRACE
   #define trace printf("%s %d -> ",__FILE__, __LINE__);printf
#else
   #define trace
#endif

int main {
  trace("Hello world");
}

C でマクロが機能する方法は、コンパイラが (本質的に)* 識別子のリテラル置換を行うことです。

したがって、あなたの場合、の値に応じて2つのオプションがあります#IFDEF

trace("Hello world");

になることができる

  1. printf("%s %d -> ",__FILE__, __LINE__);printf("Hello world");

また

  1. ("Hello world");

printf最初のオプションは、2 つのステートメントで構成される一連の有効な C コードです。2 番目のオプションは、不要な中括弧内の文字列 (char *) で構成される一連の有効な C コードです。

于 2010-04-13T14:21:47.750 に答える