避免在C预处理器中进行双宏替换

时间:2014-12-17 20:10:41

标签: c linux gcc macros

这是一个简单的小C程序,让我困惑了一段时间:

#include <stdio.h>
#define STR1(x) #x
#define STR(x) STR1(x)
int main(void) {
    printf("%s\n", STR(MYDEF));
}

这只是使用标准的字符串化双定义技术将MYDEF #define的值打印为字符串。

使用gcc -DMYDEF=abc prog.c编译(在Linux上)运行结果,并且毫不奇怪,它打印出&#39; abc&#39;。

但是更改值gcc -DMYDEF=linux prog.c并且打印的结果不是&#39; linux&#39;但是&#39; 1&#39;。

所以这让我感到困惑,但当然这是因为我发现,gcc(在Linux上)有一个内置的#define用于名称&#39; linux&#39;使用值&#39; 1&#39;,STR(x)宏最终将MYDEF扩展为&#39; linux&#39;然后linux到&#39; 1&#39;。

在我真正的程序中(这比上面的小测试复杂得多)我通过以不同(可能更好)的方式做事来绕过这个,但它让我好奇......是否有一个简单的小宏技术这将避免这种双重替换,并使程序打印出来&#39; linux&#39;?我知道我可以添加Linux的-U或#undef,但感觉有点笨拙。

我原以为所有内置的#defines都以下划线开头(通常是双下划线),但我猜不是。

2 个答案:

答案 0 :(得分:1)

我在gcc手册中看到你可以使用-ansi选项来关闭预定义的宏,例如&#34; linux&#34;

gcc -ansi -DMYDEF=linux prog.c

答案 1 :(得分:1)

没有办法只扩展一次宏,总有一个重新扫描执行进一步的替换(当然,从不递归)。在某些情况下,宏根本不会展开(与#运算符一样),这就是为什么您需要在示例中使用两个#define的额外替换级别。

在ISO C中,没有前导下划线的标识符可供您免费使用(确切地说,不是所有标识符)。 GNU C方言默认定义了一些其他宏(如linux)以实现向后兼容,但他们计划在将来删除这些宏。

要在您的计算机上获取此类宏的列表,您可以执行以下操作:

$ echo | gcc -std=gnu99 -E -dM - | grep -v '# *define  *_'
#define unix 1
#define linux 1
#define i386 1

对于较旧的Gcc,使用ISO C(-ansi / -std=c89-std=c99-std=c11 / -std=c1x的选项,这些宏未定义:

$ 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";

在ISO C模式下,这些宏正确地位于保留的命名空间中:

$ echo | gcc -std=c99 -E -dM - | grep linux
#define __linux 1
#define __linux__ 1
#define __gnu_linux__ 1