避免C预处理器中的双宏替换

tjb*_*300 5 c linux macros gcc

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

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

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

编译(在Linux上)gcc -DMYDEF=abc prog.c运行结果,毫不奇怪,它打印出'abc'.

但是更改值gcc -DMYDEF=linux prog.c并且打印的结果不是'linux'而是'1'.

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

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

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

maf*_*fso 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
Run Code Online (Sandbox Code Playgroud)

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

$ 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";
Run Code Online (Sandbox Code Playgroud)

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

$ echo | gcc -std=c99 -E -dM - | grep linux
#define __linux 1
#define __linux__ 1
#define __gnu_linux__ 1
Run Code Online (Sandbox Code Playgroud)