Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use of # in a macro [duplicate]

Please explain the code

#include <stdio.h>
#define A(a,b) a##b
#define B(a) #a
#define C(a) B(a)

main()
{
 printf("%s\n",C(A(1,2)));
 printf("%s\n",B(A(1,2)));
}

Output

12

A(1,2)

I don't understand, how the first printf evaluates to 12? Isn't it similar to the second, as C macro is simply a wrapper to B macro?

like image 617
user3612463 Avatar asked Sep 04 '16 14:09

user3612463


2 Answers

The confusion here comes from a simple rule.

When evaluating a macro the pre-processor first resolves the macros in the arguments passed to the macro. However, as a special case, if an argument is right of # or adjacent to ##, it doesn't resolve macros within such arguments. Such are the rules.

Your first case

C(A(1,2))

The pre-processor first applies the C(a) macro, which is defined as B(a). There's no # or ## adjacent to the argument in the definition (none of them in B(a) at all), thus the pre-processor must resolve macros in the argument:

A(1,2)

The definition of A(a,b) is a##b which evaluates into 12.

After the macros in the arguments of the C(a) macro are evaluated, the C macro becomes:

C(12)

The pre-processor now resolves the C(a) macro, which according to its definition becomes

B(12)

Once this is done, the pre-processor evaluates macros inside the result once again and applies the B(a) macro, so the result becomes

"12"

Your second case

B(A(1,2))

Similar to the first case, the pre-processor first applies the B(a) macro. But this time, the definition of the macro is such that the argument is preceded by #. Therefore, the special rule applies and macros inside the argument are not evaluated. Therefore, the result immediately becomes:

"A(1,2)"

The preprocessor goes over the result again trying to find more macros to expand, but now everything is a part of the string, and macros don't get expanded within strings. So the final result is:

"A(1,2)"
like image 186
PineForestRanch Avatar answered Sep 20 '22 08:09

PineForestRanch


As mentioned in Wikipedia in C-preprocessor :

The ## operator (known as the "Token Pasting Operator") concatenates two tokens into one token.

The # operator (known as the "Stringification Operator") converts a token into a string, escaping any quotes or backslashes appropriately.

If you want to stringify the expansion of a macro argument, you have to use two levels of macros:

You cannot combine a macro argument with additional text and stringify it all together. You can however write a series of adjacent string constants and stringified arguments: the C compiler will then combine all the adjacent string constants into one long string.

#define xstr(s) str(s)
#define str(s) #s
#define foo 4

str (foo)  // outputs "foo"
xstr (foo) // outputs "4"

Also, from C-FAQ Question 11.17 :

It turns out that the definition of # says that it's supposed to stringize a macro argument immediately, without further expanding it (if the argument happens to be the name of another macro).

So, similarly, going along these lines :

you're doing C(A(1,2)), 
which would roll to C(12), // since no #, so inner argument is expanded
and then to B(12)
// [since you've done two levels of macros in the code:
// 1. from C() to B(), and then, 2. B() to #a]
= 12 . 

Whereas, in the first case, only 1 level of stringification is plainly done as per definition of B(a)(since it gets stringified immediately because of #)

macro-replacement of B(A(1,2)) 
= stringification of A(1,2) 
= A(1,2).
like image 20
Am_I_Helpful Avatar answered Sep 20 '22 08:09

Am_I_Helpful