在C预处理器中避免双重宏观替换 [英] Avoiding a double macro subsitution in the C pre-processor
问题描述
#include< stdio.h>这是一个简单的小C程序,让我很困惑。
#define STR1(x)#x
#define STR(x)STR1(x)
int main(void){
printf(%s \ n, STR(MYDEF));
}
这只是将MYDEF #define的值打印为一个字符串,标准的字符串化双重定义技术。
使用编译(在Linux上)gcc -DMYDEF = abc prog.c
run结果并且毫不奇怪,它打印出'abc'。
但改变值 gcc -DMYDEF = linux prog.c
并且打印的结果不是'linux',但是'1'。
这让我困惑了一下,但是当然是因为我发现gcc(在Linux上)有一个内置的#define for名称'linux'的值为'1',STR(x)宏最终将MYDEF扩展为'linux',然后将linux扩展到'1'。
我真正的程序(这比上面的小测试更复杂)我通过以不同的(可能更好的)方式做事情来绕过这个,但它让我好奇......是否有一个简单的小宏技术可以避免这种情况双重替换并使程序打印出'linux'?我知道我可以添加一个-U或#undef的linux,但感觉有点笨拙。
我以为所有内置的#define都以下划线开头(通常是双下划线),但我猜不是。
没有办法只扩展一次宏,总会有重新扫描进行进一步的替换(当然,从不递归)。在某些情况下,根本不扩展宏(如#
运算符),这就是为什么您需要额外的替换级别以及两个#在您的示例中定义
。
在ISO C中,没有前导下划线的标识符可供您免费使用(不是所有的,确切地说)。为了向后兼容,GNU C语言默认定义了一些其他宏(比如 linux
),尽管他们计划在将来删除这些宏。
要获得您计算机上这些宏的列表,您可以执行以下操作:
$ echo | gcc -std = gnu99 -E -dM - | grep -v'#* define * _'
#define unix 1
#define linux 1
#define i386 1
使用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
#1test.c
#1< command-line>
#1test.c
1;
MYDEF;
$ gcc -std = c99 -DMYDEF = linux -E test.c
#1test.c
#1< command-line>
#1test.c
linux;
MYDEF;
在ISO C模式下,这些宏正好在保留的名称空间中:
$ echo | gcc -std = c99 -E -dM - | grep linux
#define __linux 1
#define __linux__ 1
#define __gnu_linux__ 1
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
这篇关于在C预处理器中避免双重宏观替换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!