C / C ++マクロ文字列連結


121
#define STR1      "s"
#define STR2      "1"
#define STR3      STR1 ## STR2

STR3 == "s1"を連結することは可能ですか?これを行うには、argsを別のマクロ関数に渡します。しかし、直接的な方法はありますか?


#define
STR3

これは、STR3を前処理トークンSTR1STR2として定義しているため、どちらでもありません。また、文字列リテラルを一緒に貼り付けることができないため、argsを別のマクロ関数に渡しても効果がありません。 "s" "1"は有効なトークンではありません。
ジムバルター、2011年

回答:


157

両方が文字列の場合は、次のようにできます。

#define STR3 STR1 STR2

プリプロセッサは隣接する文字列を自動的に連結します。

編集:

以下に示すように、連結を行うのはプリプロセッサーではなくコンパイラーです。


17
技術的には、文字列の連結は言語レベルで行われます。
マーティンヨーク

47
プリプロセッサはそのようなことをしません。隣接する文字列リテラルを単一の文字列リテラルであるかのように扱うのは、C言語固有です。
ジムバルター、2011年

7
それは技術以上のものです-連結L"a""b"て取得することはできませL"ab"が、連結L"a"してL"b"取得しますL"ab"
MSalters 2011年

115

文字列リテラルは言語レベルで連結されているので、このようなソリューションは必要ありません。「s」「1」は有効なプリプロセッサトークンではないため、とにかく機能しません。

[編集:残念ながらいくつかの賛成票を受け取った以下の誤った「記録のための」コメントへの応答として、私は上記のステートメントを繰り返し、プログラムの断片が

#define PPCAT_NX(A, B) A ## B
PPCAT_NX("s", "1")

gccの前処理フェーズからこのエラーメッセージが生成されます。

]

ただし、一般的なトークンの貼り付けについては、これを試してください:

/*
 * Concatenate preprocessor tokens A and B without expanding macro definitions
 * (however, if invoked from a macro, macro arguments are expanded).
 */
#define PPCAT_NX(A, B) A ## B

/*
 * Concatenate preprocessor tokens A and B after macro-expanding them.
 */
#define PPCAT(A, B) PPCAT_NX(A, B)

次に、たとえば両方 PPCAT_NX(s, 1)PPCAT(s, 1)識別子を生成s1しない限り、sケースたマクロとして定義され、PPCAT(s, 1)生成<macro value of s>1

テーマの続きはこれらのマクロです:

/*
 * Turn A into a string literal without expanding macro definitions
 * (however, if invoked from a macro, macro arguments are expanded).
 */
#define STRINGIZE_NX(A) #A

/*
 * Turn A into a string literal after macro-expanding it.
 */
#define STRINGIZE(A) STRINGIZE_NX(A)

そして、

#define T1 s
#define T2 1
STRINGIZE(PPCAT(T1, T2)) // produces "s1"

対照的に、

STRINGIZE(PPCAT_NX(T1, T2)) // produces "T1T2"
STRINGIZE_NX(PPCAT_NX(T1, T2)) // produces "PPCAT_NX(T1, T2)"

#define T1T2 visit the zoo
STRINGIZE(PPCAT_NX(T1, T2)) // produces "visit the zoo"
STRINGIZE_NX(PPCAT(T1, T2)) // produces "PPCAT(T1, T2)"

8
参考までに、"s""1"C(およびC ++)では有効です。これらは2つのトークン(文字列リテラル)であり、コンパイラーはそれ自体を連結し、1つのトークンとして脅迫します。
Shahbaz 2012

4
私のコメントとC言語の両方を誤解している。私は言った"s""1" isn't a valid token-それは正しいです。あなたが言うように、それは2つのトークンです。しかし、それらを##で結合すると、2つのトークンではなく単一の前処理トークンになるため、コンパイラーは連結を行わず、レクサーがそれらを拒否します(言語には診断が必要です)。
ジムバルター2012

8
@ mr5コメントを注意深く読んでください。マクロ引数として渡されるマクロ名は、渡される前に展開されません。ただし、マクロの本体で展開されます。したがって、AがFREDとして定義されている場合、STRINGIZE_NX(A)は "A"に拡張されますが、STRINGIZE(A)は "FRED"に拡張されるSTRINGIZE_NX(FRED)に拡張されます。
ジムバルター2014

1
@bharath 結果の文字列は "PPCAT(T1、T2)"です -期待どおりに。期待される「s1」ではありません -まったく期待されていません。なぜ追加の間接化/ネストが必要なのですか?-コードのコメントと、上記の6つの賛成票を含むコメントを読んでください。マクロの本体のみが展開されます。マクロ本体の外では、括弧に囲まれたマクロ引数はマクロに渡される前に展開されません。そのSTRINGIZE_NX(whatever occurs here)ため、何が発生したか、またはここでのマクロ定義に関係なく、「ここで発生したもの」に展開されます。
ジムBalter

1
@bharathもちろん、「名前A」は出力されません-Aはパラメーター名であり、マクロへの引数ではなく、ALEXです。あなたは主張しましたif A is defined as FRED then STRINGIZE_NX(A) still expands to "FRED"-それは誤りであり、あなたのテストに似ていません。あなたはこれを理解したり正しく理解したりしないように一生懸命努力しています、そして私はあなたにこれ以上応答しません。
ジムBalter

24

ヒント:上記のSTRINGIZEマクロはすばらしいですが、間違いがあり、その引数がマクロではない場合-名前にタイプミスがあるか#include、ヘッダーファイルを忘れた場合、コンパイラーは意図したマクロ名をエラーのない文字列。

への引数STRINGIZEが常に通常のC値を持つマクロであることを意図している場合、

#define STRINGIZE(A) ((A),STRINGIZE_NX(A))

一度展開して有効性を確認し、それを破棄してから、もう一度文字列に展開します。

なぜ私が含まれていなかったのかでSTRINGIZE(ENOENT)"ENOENT"なく、なぜ終わるのかを理解するのにしばらく時間がかかりました。"2"errno.h


2
重要な観察、および,オペレーターの適切な使用のための+1 。:)
ジェシーチザム2015

2
文字列の内容が有効なC式である必要がある特別な理由はありません。これを行う場合は、STRINGIZE_EXPRなどの別の名前を付けることをお勧めします。
ジムBalter

そのトリックは単独で機能した可能性があります。しかし、それはコンパイラーが連結する一連のストリングを見るのを防ぎます。(((1),"1") "." ((2),"2")単に "1" "。" "2"ではなくのようなシーケンスになります)
自己変形

オートモーフィックが何を言っているかを明確にするために:元のSTRINGIZE定義では"The value of ENOENT is " STRINGIZE(ENOENT)機能"The value of ENOENT is" STRINGIZE_EXPR(X)するが、エラーを生成する。
ジムバルター
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.