您的位置:首页 > 编程语言 > C语言/C++

C/C++:宏定义中的#与##

2017-05-04 01:05 225 查看
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: