代码改变世界

怎样安全的使用可变参数宏__VA_ARGS__

2012-10-11 20:38  menggucaoyuan  阅读(591)  评论(0编辑  收藏

  今天遇到一个关于可变参数的问题,下面先给出一个示例程序说明我遇到的问题。
0 #include <stdio.h>
1
2 #define print(fmt, ...) out(fmt, __VA_ARGS__)
3 #define out(fmt, ...) printf(fmt, __VA_ARGS__)
4
5 int main(int argc, char** argv)
6 {
7 print("hello %d\n", 0);
8 print("hello\n");
9 return 0;
10 }
  为了说明问题,上面有两个print宏调用,出问题的是第二个。 用gcc编译的时候给出错误提示:"7:2: error: expected expression before ')' token"。

  gcc的错误提示说明第8行的无参数的宏print调用是错误的。原因清晰,第八行的"hello\n"后面没有给出参数,导致"printf(fmt, __VA_ARGS__)"编译后成了"printf("hello\n", )",原因很清楚了。

  那么,为了去掉gcc 编译错误,我是不是得把第8行代码改成第7行的样式凑合着用?
  经过半天google,在 http://hi.baidu.com/pragmatist/item/591a8a4ccbd643086dc2f035 得到了答案(补充一下,百度真垃圾,自己搜自己的网页,还是没有给出我想要的答案)。文中有提示"##符号在 逗号 和 参数名 之间时不做连字符作用,而作为变参宏的特别用处",我急忙把程序修改成如下代码:
1 #include <stdio.h>
2
3 #define print(fmt, ...) out(fmt, __VA_ARGS__)
4 #define out(fmt, ...) printf(fmt, ##__VA_ARGS__)
5
6 int main(int argc, char** argv)
7 {
8 print("hello %d\n", 0);
9 print("hello\n");
10 return 0;
11 }
  我只修改了第4行的代码,此时gcc仍然给我相同的提示,难道我用错了?没办法了,只好找来同事帮忙。同事很给力了,一见代码,就见了亲人,说,再把第三行修改一下。最终代码如下:
1 #include <stdio.h>
2
3 #define print(fmt, ...) out(fmt, ##__VA_ARGS__)
4 #define out(fmt, ...) printf(fmt, ##__VA_ARGS__)
5
6 int main(int argc, char** argv)
7 {
8 print("hello %d\n", 0);
9 print("hello\n");
10 return 0;
11 }

  通过gcc -E test.c,得到如下代码:



int main(int argc, char** argv)
{
 printf("hello %d\n", 0);
 printf("hello\n");
 return 0;
}
  此时,预编译后的代码中第8行的结果中没有烦人的逗号,一切都很美好,gcc的错误提示没有了。再次领略静心细心的重要性。

  建议以后使用变参__VA_ARGS__都以##__VA_ARGS__的形式使用。