The C11 standard says (and other versions of C, and C++, say similarly):
A preprocessing directive of the form # define identifier replacement-list new-line defines an object-like macro that causes each subsequent instance of the macro name to be replaced by the replacement list of preprocessing tokens that constitute the remainder of the directive. The replacement list is then rescanned for more macro names as specified below.
However it also says in another part (thanks to rici for pointing this out).
The preprocessing tokens within a preprocessing directive are not subject to macro expansion unless otherwise stated.
So a subsequent instance of the macro name which is found inside another #define directive is actually not replaced.
Your line #define FOO NUM defines that when the token FOO is later found (outside of another #define directive!), it will be replaced by the token NUM .
After a token is replaced, rescanning occurs, and if NUM is itself a macro, then NUM is replaced at that point. (And if whatever NUM expands to contains macros , then that gets expanded , and so on).
So your sequence of steps is actually:
NUM defined as 10
FOO defined as NUM
NUM undefined and re-defined as 20
FOO expands to NUM
- (rescan)
NUM expands to 20
This behaviour can be seen in another common preprocessor trick, to turn the defined value of a macro into a string:
#define STR(X) #X
#define STR_MACRO(X) STR(X)
#define NUM 10
puts( STR_MACRO(NUM) ); // output: 10
If we had written puts( STR(NUM) ) then the output would be NUM.
The output of 10 is possible because, as before, the second #define here does not actually expand out STR. So the sequence of steps in this code is:
STR(X) defined as #X
STR_MACRO(X) defined as STR(X)
NUM defined as 10
STR_MACRO and NUM are both expanded; the result is puts( STR(10) );
- (Rescan result of last expansion)
STR(10) is expanded to "10"
- (Rescan result of last expansion) No further expansion possible.