Linux:C/C++接受可变参数的宏

97 篇文章 7 订阅

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




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值