Here's a simple little C program that had me confused for a while:
#include <stdio.h>
#define STR1(x) #x
#define STR(x) STR1(x)
int main(void) {
printf("%s\n", STR(MYDEF));
}
This just prints the value of the MYDEF #define as a string, using the standard stringizing double-define technique.
Compile (on Linux) with gcc -DMYDEF=abc prog.c
run the result and, not surprisingly, it prints out 'abc'.
But change the value gcc -DMYDEF=linux prog.c
and the result printed is not 'linux' but '1'.
So that confused me for a bit, but of course it happens because gcc (on Linux) has, I discovered, a built-in #define for the name 'linux' with a value '1', and the STR(x) macro ends up expanding MYDEF to 'linux' then linux to '1'.
In my real program (which was rather more complex than the little test above) I got round this by doing things in a different (probably better) way, but it left me curious ... is there a simple little macro technique that would avoid this double-substitution and make the program print out 'linux'? I know I could add a -U or #undef of linux, but that feels a bit clumsy.
I had thought all the built-in #defines start with underscores (usually double underscores), but I guess not.
There is no way to expand a macro only once, there's always a rescan performing further replacement (never recursive, of course). There are circumstances where macros aren't expanded at all (as with the #
operator), which is why you need the extra replacement level with two #define
like in your example.
In ISO C, identifiers without a leading underscore are free for you to use (not all of them, to be precise). The GNU C dialects define some other macros by default (like linux
) for backwards compatibility, though they plan to remove such macros in the future.
To get a list of such macros on your machine, you can do:
$ echo | gcc -std=gnu99 -E -dM - | grep -v '# *define *_'
#define unix 1
#define linux 1
#define i386 1
With the options for ISO C (-ansi
/-std=c89
, -std=c99
, -std=c11
/-std=c1x
for older Gcc), these macros are not defined:
$ cat test.c
#define STR1(x) #x
#define STR(x) STR1(x)
STR(MYDEF);
STR1(MYDEF);
$ gcc -std=gnu99 -DMYDEF=linux -E test.c
# 1 "test.c"
# 1 "<command-line>"
# 1 "test.c"
"1";
"MYDEF";
$ gcc -std=c99 -DMYDEF=linux -E test.c
# 1 "test.c"
# 1 "<command-line>"
# 1 "test.c"
"linux";
"MYDEF";
In ISO C mode, these macros properly are in the reserved namespace:
$ echo | gcc -std=c99 -E -dM - | grep linux
#define __linux 1
#define __linux__ 1
#define __gnu_linux__ 1
I see in the gcc manual that you can use the -ansi option to turn off predefined macros like "linux"
gcc -ansi -DMYDEF=linux prog.c
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With