The effect of a single hash in an object-like macro

Is # permitted in an object-like macro, and if so, what happens?

The C standard defines # macro behavior for functionally similar macros.

Code example:

 #include <stdio.h> #define AX#Y #define B(X) #X #define C(X) B(X) int main() { printf(C(A) "\n"); } 

gcc displays X#Y , suggesting that it allow # be present and not perform any special processing. However, since the definition of the # operator does not define behavior in this case, is this behavior undefined?

+5
source share
1 answer

As you noticed, # has only a certain effect in functional macros. § 6.10.3.2/1 (all references to the standard refer to draft C11 ( N1570 )). To find out what happens in object-oriented macros, we have to look elsewhere.

Form preprocessing directive

 # define identifier replacement-list new-line 

defines an object-like macro that calls each subsequent instance of the macro name is replaced by a replacement list of preprocessing tokens that make up the rest of the directive. [...]

§ 6.10.3 / 9

So the only question is whether # allowed in replacement-list . If so, he takes part in the replacement, as usual.

We find the syntax in § 6.10 / 1:

 replacement-list: pp-tokens (opt.) pp-tokens: preprocessing-token pp-tokens preprocessing-token 

Now, # valid preprocessing-token ? Section 6.4 / 1 states:

 preprocessing-token: header-name identifier pp-number character-constant string-literal punctuator each non-white-space character that cannot be one of the above 

This, of course, is not a header-name (clause 6.4.7 / 1), it is not allowed in identifier tokens (§ 6.4.2.1/1), nor is it a pp-number (which, in principle, is any number allowed format, § 6.4.8 / 1), as well as character-constant (for example, u'c' , § 6.4.4.4/1) or string-literal (exactly what you expect, for example L"String" , § 6.4 .5 / 1).

However, it is indicated as a punctuator in 6.4.6 / 1. Therefore, it is allowed in the replacement-list object-like macro and will be copied verbatim. Now it is re-scanned as described in 6.10.3.4. Let's look at your example:

C(A) will be replaced by C(X#Y) . # here does not have much effect, since it is not in the replacement-list from C , but its argument. C(X#Y) obviously turns into B(X#Y) . Then, argument B converted to a string literal using the # operator in B replacement-list , giving "X#Y"

Therefore, you do not have undefined behavior.

+3
source

All Articles