web-dev-qa-db-ja.com

マクロ内の#および##

  #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$

なぜそうなのですか?

34
algo-geeks

それはプリプロセッサが機能する方法だからです。

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

前処理された出力(たとえば、gcc -E)マクロの評価方法をよりよく理解したい場合。

31
Christoffer

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

27
Joe Bloggs

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

引数プレスキャン

マクロ引数は完全にマクロ展開されますbeforeそれらがstringifiedまたはpastedと他のトークン。置換後、置換された引数を含むマクロ本体全体がスキャンされ再びマクロが拡大。その結果、引数が2回スキャンされ、マクロ呼び出しが展開されます。

文字列化

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

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

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

マクロを展開するときに、2つのトークンを1つにマージすると便利な場合があります。これはtoken pasteingまたはトークン連結。 「##」前処理演算子はトークンの貼り付けを実行します。マクロが展開されると、各「##」演算子の両側にある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

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.
21
smwikipedia