Schauen wir uns mal dieses File an:
#define CPP1(s) #s
#define CPP2(s) CPP1(s)
char *a = CPP1(HELLO);
char *b = CPP2(HELLO);
CPP1() benutzt #s, den Operator im C-Preprozessor, der einen Namen in einen String umwandelt.
Eigentlich sollte man ja erwarten, das CPP1 und CPP2 identisch sind. Auf der ersten Blick ist das auch so:
$ gcc -E t.c
#...
char *a = "HELLO";
char *b = "HELLO";
Aber wenn man HELLO als Symbol definiert, ändert sich das Bild:
$ gcc -D HELLO=WORLD -E t.c
# ...
char *a = "HELLO";
char *b = "WORLD";
Wieder mal was über den C-Preprozessor gelernt.