46
  #include <stdio.h>
  #define f(a,b) a##b
  #define g(a)   #a
  #define h(a) g(a)

  int main()
  {
    printf("%s\n",h(f(1,2)));
    printf("%s\n",g(f(1,2)));
    return 0;
  }

プログラムを見るだけで、両方のprintfステートメントで同じ出力が期待される可能性があります。しかし、プログラムを実行すると、次のようになります。

bash$ ./a.out
12
f(1,2)
bash$

なんでそうなの?

4

3 に答える 3

40

関数のようなマクロでのパラメーターの出現は、それがまたはのオペランドでない限り、それ###置換してさらに拡張するために全体を再スキャンする前に拡張されます。gのパラメータのオペランドであるため#、引数は展開されず、代わりにすぐに文字列化されます("f(1,2)")。hのパラメータはnorのオペランドではないため、引数は最初に展開され()、次に置換され()、次に再スキャンされ、さらに展開が行われます()。###12g(12)"12"

于 2010-12-06T17:06:14.733 に答える
39

それがプリプロセッサの仕組みだからです。

単一の「#」は、その引数に何が含まれているかに関係なく、指定された引数から文字列を作成し、二重の「##」は、引数を連結して新しいトークンを作成します。

gcc -Eマクロがどのように評価されるかをよりよく理解したい場合は、前処理された出力(たとえば、を使用)を見てみてください。

于 2010-12-06T10:18:31.330 に答える
27

以下は、あなたの質問に関連するいくつかの概念です。

引数プレスキャン

マクロ引数は 、他のトークンで文字列化または貼り付けされていない限り、マクロ本体に置き換えられる前に完全にマクロ展開されます。置換後、置換された引数を含むマクロ本体全体が再度スキャンされ、展開されるマクロが検出されます。その結果、引数が2回スキャンされ、マクロ呼び出しが展開されます。

文字列化

マクロパラメータが先頭の「#」とともに使用される場合、プリプロセッサはそれを文字列定数に変換された実際の引数のリテラルテキストに置き換えます。

#ABC => "ABC" <----文字列化プロセスによって追加される、囲んでいる二重引用符に注意してください。

トークンの貼り付け/トークンの連結

マクロを展開するときに、2つのトークンを1つにマージすると便利なことがよくあります。これは、トークンの貼り付けまたはトークンの連結と呼ばれます。'##'前処理演算子は、トークンの貼り付けを実行します。マクロが展開されると、各「##」演算子の両側にある2つのトークンが1つのトークンに結合され、マクロ展開の「##」と2つの元のトークンが置き換えられます。

したがって、シナリオの詳細なプロセスは次のようになります。

h(f(1,2))
-> h(12) // f(1,2) pre-expanded since there's no # or ## in macro h
-> g(12)  // h expanded to g
"12"   // g expanded as Stringification

g(f(1,2))
-> "f(1,2)"  //f(1,2) is literally strigified because of the `#` in macro g. f(1,2) is NOT expanded at all.
于 2016-09-11T07:50:01.213 に答える