An occurrence of a parameter in a function-like macro, unless it is the operand of
##, is expanded before substituting it and rescanning the whole for further expansion. Because
g‘s parameter is the operand of
#, the argument is not expanded but instead immediately stringified (
h‘s parameter is not the operand of
##, the argument is first expanded (
12), then substituted (
g(12)), then rescanning and further expansion occurs (
Because that is how the preprocessor works.
A single ‘#’ will create a string from the given argument, regardless of what that argument contains, while the double ‘##’ will create a new token by concatenating the arguments.
Try looking at the preprocessed output (for instance with
gcc -E) if you want to understand better how the macros are evaluated.
Below are some related concepts to your question:
Macro arguments are completely macro-expanded before they are
substituted into a macro body, unless they are stringified or pasted
with other tokens. After substitution, the entire macro body,
including the substituted arguments, is scanned again for macros to be
expanded. The result is that the arguments are scanned twice to expand
macro calls in them.
When a macro parameter is used with a leading ‘#’, the preprocessor
replaces it with the literal text of the actual argument, converted to
a string constant.
#ABC => "ABC" <—- Note the enclosing double quote, which is added by the stringification process.
Token Pasting / Token Concatenation:
It is often useful to merge two tokens into one while expanding
macros. This is called token pasting or token concatenation. The ‘##’
preprocessing operator performs token pasting. When a macro is
expanded, the two tokens on either side of each ‘##’ operator are
combined into a single token, which then replaces the ‘##’ and the two
original tokens in the macro expansion.
So the detailed process of your scenario is like this:
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.