Linux:C/C++接受可变参数的宏
注:下面的宏定义最好写成大写,我写成了小写debug,需要注意。
测试环境: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)
在Linux下,C/C++的宏可以接受可变参数,就像函数一样的形式。
常用的printf族函数,都可以接受可变参数:
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);
其中那三个点就代表可变参数。
C99编译器标准允许宏接受可变参数:
#define debug(...) printf(__VA_ARGS__)
#include <stdio.h>
#include <stdlib.h>
#define debug(...) printf(__VA_ARGS__)
int main()
{
debug("%s\n", "this is a test");
return 0;
}
输出如下:
[mytmp@localhost ~]$ gcc -o main main.c
[mytmp@localhost ~]$ ./main
this is a test
……
int main()
{
printf("%s\n", "this is a test");
return 0;
}
在此例中,...代表的是:
"%s\n", "this is a test"
最后会使用上面的内容替换掉printf中的__VA_ARGS__。
我们称debug这样的宏,就是接受可变参数的宏。
注:有可能你的编译器不支持可变参数的宏。
我们写程序时,会写很多的调试信息,比如printf。
在最后发布时,我们会把这些printf都一一删除,但是没有人可以保证程序准确无误,一旦有bug产生,我们还是需要很多printf来帮助。
你是说你再一一地将这些printf加到你想要的位置?不觉得很麻烦还是一件苦力劳动吗?
首先我们想到可以借助一个开关。
#include <stdio.h>
#include <stdlib.h>
#define DEBUG
int main()
{
#ifdef DEBUG
printf("%s\n", "this is a test!");
#endif
return 0;
}
[mytmp@localhost ~]$ !g
gcc -o main main.c
[mytmp@localhost ~]$ ./main
this is a test!
当我们不需要调试的时候可以修改开关的值,比如说将#define DEBUG删除,重新编译,以达到不输出调试信息的效果。
我们假设有100句printf,那么意味着我们需要写100*3=300句,有没有更简单的方式?
#include <stdio.h>
#include <stdlib.h>
#define DEBUG
#ifdef DEBUG
#define debug(s) printf(s)
#else
#define debug(s)
#endif
int main()
{
debug("this is a test\n");
return 0;
}
这样子就好多了,还是只需要一句话(debug)就达到调试目的。
话说,如果有很多个参数呢?每个参数类型不同呢?此时上面的宏已经无法支持了,也就需要我们刚刚说的可变参数宏。
以下是两种方式:
方法一:
#include <stdio.h>
#include <stdlib.h>
#define DEBUG
#ifdef DEBUG
#define debug(format, args...) printf(format, ##args)
#else
#define debug(format, args...)
#endif
int main()
{
debug("%s\n", "this is a test");
return 0;
}
说明:
args...中,args是一个可变参数(或者说可变参数列表)的名字,也就是说,把那么一大堆的东西给他起了个形参似的名字,叫做args,这样写增强了可读性,可描述性。
还有一点,为什么args前面有##呢?这是干啥用的?
假设我们的代码是这样子的:
#include <stdio.h>
#include <stdlib.h>
#define DEBUG
#ifdef DEBUG
#define debug(format, args...) printf(format, args)
#else
#define debug(format, ...)
#endif
int main()
{
debug("this is a test !!\n");
return 0;
}
最后预编译完成是这样子的:
……
# 3 "main.c" 2
# 12 "main.c"
int main()
{
printf("this is a test !!\n", );
return 0;
}
很明显printf中多了个逗号呀。
使用##可以消除这种错误。
#ifdef DEBUG
#define debug(format, args...) printf(format, ##args)
#else
#define debug(format, ...)
#endif
预编译之后变成了:
……
__attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1)));
# 977 "/usr/include/stdlib.h" 3 4
# 3 "main.c" 2
# 12 "main.c"
int main()
{
printf("this is a test !!\n");
return 0;
}
如果可变参数是空,那么##可以使得其前面的逗号去掉。
或者:
方法二:
#include <stdio.h>
#include <stdlib.h>
#define DEBUG
#ifdef DEBUG
#define debug(format, ...) printf(format, __VA_ARGS__)
#else
#define debug(format, ...)
#endif
int main()
{
debug("%s\n", "this is a test");
return 0;
}
最好是:
#include <stdio.h>
#include <stdlib.h>
#define DEBUG
#ifdef DEBUG
#define debug(format, ...) printf(format, ##__VA_ARGS__)
#else
#define debug(format, ...)
#endif
int main()
{
debug("this is a test !!!\n");
return 0;
}
##__VA_ARGS__,消除空前面的逗号。
回到原点,我们想做一个比较完善的宏,用以调试。
如果可以顺便输出文件名和行号就好了:我们可以借助__LINE__以及__FILE__两个宏定义。
#include <stdio.h>
#include <stdlib.h>
#define DEBUG
#ifdef DEBUG
#define debug(format, ...) printf(format, ##__VA_ARGS__)
#else
#define debug(format, ...)
#endif
int main()
{
debug("[%s][%d]:%s", __FILE__, __LINE__, "this is a test!!!!\n");
return 0;
}
输出如下:
[mytmp@localhost ~]$ ./main
[main.c][14]:this is a test!!!!
每次都写一长串__LINE__,__FILE__不觉得累吗?
#include <stdio.h>
#include <stdlib.h>
#define DEBUG
#ifdef DEBUG
#define debug(format, ...) printf("[%s][%d]:"format, __FILE__, __LINE__, ##__VA_ARGS__)
#else
#define debug(format, ...)
#endif
int main()
{
debug("%s", "this is a test!!!!!\n");
debug("hello, world!\n");
return 0;
}
预编译后如下:
……
# 961 "/usr/include/stdlib.h" 3 4
extern int getloadavg (double __loadavg[], int __nelem)
__attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1)));
# 977 "/usr/include/stdlib.h" 3 4
# 3 "main.c" 2
# 12 "main.c"
int main()
{
printf("[%s][%d]:""%s", "main.c", 14, "this is a test!!!!!\n");
printf("[%s][%d]:""hello, world!\n", "main.c", 15);
return 0;
}
注意,printf("[%s][%d]:""%s", "main.c", 14, "this is a test!!!!!\n");这样两个字符串直接挨在一起是合法的,会自动合并成一个字符串。
输出如下:
[mytmp@localhost ~]$ ./main
[main.c][14]:this is a test!!!!!
[main.c][15]:hello, world!
这样子,debug基本完成了,拥有自动追加文件名和行号的功能,以及格式化可变参数如同printf那样输出。
日志里面使用,再好不过啦!
资料参考:
1.http://blog.csdn.net/aobai219/article/details/6092292
2.http://www.cnblogs.com/lixiaohui-ambition/archive/2012/08/21/2649052.html