C/C++ Macro string concatenation

2019-01-01 08:41发布

问题:

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

Is it possible to concatenate have STR3 == \"s1\"? You can do this by passing args to another Macro function. But is there a direct way?

回答1:

If they\'re both strings you can just do:

#define STR3 STR1 STR2

The preprocessor automatically concatenates adjacent strings.

EDIT:

As noted below, it\'s not the preprocessor but the compiler that does the concatenation.



回答2:

You don\'t need that sort of solution for string literals, since they are concatenated at the language level, and it wouldn\'t work anyway because \"s\"\"1\" isn\'t a valid preprocessor token. However, for general token pasting, try this:

/*
 * 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)

Then, e.g., PPCAT(s, 1) produces the identifier s1.

Continuing on the theme are these macros:

/*
 * 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)

Then,

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


回答3:

Hint: The STRINGIZE macro above is cool, but if you make a mistake and its argument isn\'t a macro - you had a typo in the name, or forgot to #include the header file - then the compiler will happily put the purported macro name into the string with no error.

If you intend that the argument to STRINGIZE is always a macro with a normal C value, then

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

will expand it once and check it for validity, discard that, and then expand it again into a string.

It took me a while to figure out why STRINGIZE(ENOENT) was ending up as \"ENOENT\" instead of \"2\"... I hadn\'t included errno.h.