The C standard has a special terminology for how macros are expanded.
Macro names are, in effect, stored in a big table of "all macros defined at this time". Each table entry "macro name" on the left (and any arguments in the middle) and "expansion token-stream" on the right.
When a macro is to be expanded (because it occurs in some non-preprocessor line, or in a position in a preprocessor line in which it must be expanded -- e.g., you can #define STDIO <stdio.h> and then #include STDIO), the table entry is "painted blue", and then the replacement token-stream is read (with argument expansion as also dictated by the standard).
If the replacement token-stream contains the original macro name, it no longer matches, because the "blue paint" covers up the name.
When the replacement token-stream has been fully handled, the "blue paint" is removed, re-exposing the name.
Thus:
#define X 5
adds X: (no arguments), 5 to the table.
Then:
#define Y X
adds: Y: (no arguments), X to the table.
Somewhere later in the file, you might have an occurrence of the token Y. Assuming neither of the above have been #undefed (removed from the table), the compiler must first "paint the table entry for Y blue" and replace the token Y with the token X.
Next, the compiler must "paint the table entry for X blue" and replace the token X with the token 5.
The token 5 is not a preprocessor macro name (it can't be by definition) so the token 5 passes beyond the reach of the preprocessing phase. Now the "blue paint" is removed from the X table entry, as that's done; and then the "blue paint" is removed from the Y entry, which is also done.
If you were to write instead:
#define Y Y, Y, Y, the letter is called Y!
then the sequence, on encountering a later token Y, would be:
- paint entry for
Y blue
- drop in replacement token sequence:
Y , Y , Y , the letter is called Y !
- check each replacement token in the table -- since
Y is painted blue those don't match, and , cannot match, so those are all passed on to the rest of the compiler; the, letter, is, and called must be checked but are probably not in the table and hence passed on; Y is still painted blue so does not match and is passed on, and ! cannot match and is passed on.
- remove blue paint, restoring the expansion