C/C++:宏定义中的#与##
测试环境:CentOS
[mytmp@localhost ~]$ uname -a Linux localhost.localdomain 2.6.18-371.el5 #1 SMP Thu Sep 5 21:21:44 EDT 2013 x86_64 x86_64 x86_64 GNU/Linux [mytmp@localhost ~]$ gcc --version gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-54)
在宏定义中,使用#可以将参数变成一个字符串,使用##可以将参数粘合在一起。
#include <stdio.h> #include <stdlib.h> #define NUM(n) #n #define MYPASTE(n, m) n##01##m int main() { printf("%d\n", 10); printf("%s\n", NUM(11)); printf("%d\n", MYPASTE(2, 7)); return 0; } [mytmp@localhost ~]$ gcc -E main.c …… int main() { printf("%d\n", 10); printf("%s\n", "11"); printf("%d\n", 2017); return 0; } 输出如下: [mytmp@localhost ~]$ ./main 10 11 2017 可以看到#起到了将一个数字转变为字符串的功能;
##可将多部分直接黏连在一起。
#以及##出现在宏定义中的展开问题:
#include <stdio.h> #include <stdlib.h> #define NUM 6 #define S_NUM(num) #num #define PASTE_NUM(a, b) a##b int main() { printf("%s\n", S_NUM(NUM)); printf("%d\n", PASTE_NUM(NUM, NUM)); return 0; } [mytmp@localhost ~]$ gcc -E main.c
预编译输出如下:
…… int main() { printf("%s\n", "NUM"); printf("%d\n", NUMNUM); return 0; } 这个例子什么意思呢?
现在有个宏是NUM,当它传入到另外一个宏:S_NUM中时,会不会展开。
显然是不会的,因为在S_NUM的定义中使用了#,即后半部分:#num。
到最后就是#NUM,NUM不会被替换为对应的数字(6),不会展开。
简单点来说:#MACRO时,MACRO是一个宏,MACRO不会展开。
##同理。
##可以看到最后直接是符号级别的黏连在一起了,并没有展开为66(NUM##NUM-》NUM)。
有没有展开的办法?--》多使用一层宏来展开:
#include <stdio.h> #include <stdlib.h> #define NUM 6 #define S_NUM(num) #num #define S_NUM1(num) S_NUM(num) #define PASTE_NUM(a, b) a##b #define PASTE_NUM1(a, b) PASTE_NUM(a, b) int main() { printf("%s\n", S_NUM1(NUM)); printf("%d\n", PASTE_NUM1(NUM, NUM)); return 0; } 预编译之后为:
…… int main() { printf("%s\n", "6"); printf("%d\n", 66); return 0; } 输出结果为:
[mytmp@localhost ~]$ gcc -o main main.c [mytmp@localhost ~]$ ./main 6 66 很明显,多加了一层展开了宏。
有可能是这么一个流程:首先遇到了S_NUM1(NUM),看了下S_NUM1的宏定义,没有啥#开头的,那么就用NUM替换掉S_NUM1,这样就是S_NUM(6),接下来发现是#num,再将其转换为字符串。
核心的是:增加的那一层,没有#/##,使得宏参数展开,继而不会产生#/##出现展不开的问题。
另一个不再赘述。
现在我们看一个例子。
有这么一个宏:
#include <stdio.h> #include <stdlib.h> #define AT __FILE__":"__LINE__ int main() { printf("%s\n", AT); return 0; } 本意是输出__FILE__以及__LINE__,但是实际预编译后为:
int main() { printf("%s\n", "main.c"":"8); return 0; } 究其原因,是__LINE__并不是字符串,而是个数字。
呀,我可以使用#来将其转换下:
#define AT __FILE__":"#__LINE__ 预编译是:
printf("%s\n", "main.c"":"#8); 换个宏定义试试:
#include <stdio.h> #include <stdlib.h> #define CONVERT(x) #x #define AT __FILE__":"CONVERT(__LINE__) int main() { printf("%s\n", AT); return 0; } 预编译后是:
int main() { printf("%s\n", "main.c"":""__LINE__"); return 0; } 结果并不奇怪,仔细想想,宏展开了嘛?当然没有。我们之前说怎么展开来着?
增加一层“宏参数展开层”。
#include <stdio.h> #include <stdlib.h> #define CONVERT(x) #x #define CONVERT1(x) CONVERT(x) #define AT __FILE__":"CONVERT1(__LINE__) int main() { printf("%s\n", AT); return 0; } 预编译之后如下:
int main() { printf("%s\n", "main.c"":""10"); return 0; } 输出结果如下:
[mytmp@localhost ~]$ ./main main.c:10
成功。
参考资料:
1.http://blog.csdn.net/tomtc123/article/details/8875468
2.http://www.cnblogs.com/lzjsky/archive/2010/11/24/1886690.html
3.http://www.blogjava.net/cxzforever/articles/356583.html