linux:c/c 接受可变参数的宏-尊龙官方平台

linux:c/c 接受可变参数的宏

el/2024/3/25 17:05:54

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 
#include #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 
#include #define debugint main()
{
#ifdef debugprintf("%s\n", "this is a test!");
#endifreturn 0;
}

[mytmp@localhost ~]$ !g
gcc -o main main.c
[mytmp@localhost ~]$ ./main
this is a test!

当我们不需要调试的时候可以修改开关的值,比如说将#define debug删除,重新编译,以达到不输出调试信息的效果。


我们假设有100句printf,那么意味着我们需要写100*3=300句,有没有更简单的方式?


#include 
#include #define debug#ifdef debug
#define debug(s) printf(s)
#else
#define debug(s)
#endifint main()
{debug("this is a test\n");return 0;
}

这样子就好多了,还是只需要一句话(debug)就达到调试目的。


话说,如果有很多个参数呢?每个参数类型不同呢?此时上面的宏已经无法支持了,也就需要我们刚刚说的可变参数宏。


以下是两种方式:


方法一:

#include 
#include #define debug#ifdef debug
#define debug(format, args...) printf(format, ##args)
#else
#define debug(format, args...)
#endifint main()
{debug("%s\n", "this is a test");return 0;
}


说明:


args...中,args是一个可变参数(或者说可变参数列表)的名字,也就是说,把那么一大堆的东西给他起了个形参似的名字,叫做args,这样写增强了可读性,可描述性。

还有一点,为什么args前面有##呢?这是干啥用的?


假设我们的代码是这样子的:


#include 
#include #define debug#ifdef debug
#define debug(format, args...) printf(format, args)
#else
#define debug(format, ...)
#endifint 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 
#include #define debug#ifdef debug
#define debug(format, ...) printf(format, __va_args__)
#else
#define debug(format, ...)
#endifint main()
{debug("%s\n", "this is a test");return 0;
}

最好是:


#include 
#include #define debug#ifdef debug
#define debug(format, ...) printf(format, ##__va_args__)
#else
#define debug(format, ...)
#endifint main()
{debug("this is a test !!!\n");return 0;
}

##__va_args__,消除空前面的逗号。


回到原点,我们想做一个比较完善的宏,用以调试。

如果可以顺便输出文件名和行号就好了:我们可以借助__line__以及__file__两个宏定义。


#include 
#include #define debug#ifdef debug
#define debug(format, ...) printf(format, ##__va_args__)
#else
#define debug(format, ...)
#endifint 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 
#include #define debug#ifdef debug
#define debug(format, ...) printf("[%s][%d]:"format, __file__, __line__, ##__va_args__)
#else
#define debug(format, ...)
#endifint 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





http://www.ngui.cc/el/5127169.html

相关文章

c/c :宏定义中的#与##

c/c:宏定义中的#与## 测试环境:centos [mytmplocalhost ~]$ 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 [mytmplocalhost ~]$ gcc --version gcc (gcc) 4.1.2 20080704 (red hat 4.1…

c/c :__line__;__file__;__date__;__time__;__function__等

c/c:__line__;__file__;__date__;__time__;__function__等 测试环境:centos [mytmplocalhost ~]$ 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 [mytmplocalhost ~]$ gcc --version gc…

linux:valgrind使用

linux:valgrind使用 本着实践主义和实用主义,开始学习。 测试环境:centos 7 [test1280localhost 20170504]$ uname -a linux localhost.localdomain 3.10.0-327.el7.x86_64 #1 smp thu nov 19 22:10:57 utc 2015 x86_64 x86_64 x86_64 gnu…

约瑟夫环(josephproblem)

约瑟夫环(josephproblem) 题目: 有41个人围坐成一圈玩游戏,编号分别是0,1,2,…,39,40. 从1开始,每次数到3的人就退出游戏,下个人再次从1开始。 请问最后剩下的人的编号? 约瑟夫环有很多解题…

c/c :使用数组模拟链表

c/c:使用数组模拟链表 链表中每一个node都由两部分组成: 1.真实的数据; 2.下一个数据的位置; 通常的实现可以使用指针作为指示下一个数据的位置的方式,当没有一个合法的数据存在,就将指针部分置为null&…

c/c :全排列问题

c/c:全排列问题 啥叫全排列? 从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。 当mn时所有的排列情况叫全排列。 假设现在有三个数字:…

杭电-pid1181-变形课

杭电-pid1181-变形课 problem description 呃......变形课上harry碰到了一点小麻烦,因为他并不像hermione那样能够记住所有的咒语而随意的将一个棒球变成刺猬什么的,但是他发现了变形咒语的一个统一规律:如果咒语是以a开头b结尾的一个单词,那么它的作用就恰好是使a物体变成b物…

杭电-pid1008-elevator

杭电-pid1008-elevator 好吧,其实这道题是水过去的。。 problem description the highest building in our city has only one elevator. a request list is made up with n positive numbers. the numbers denote at which floors the elevator will stop, in s…

杭电-pid1012-u calculate e

杭电-pid1012-u calculate e #include #include int main() {printf("n e\n- -----------\n0 1\n1 2\n2 2.5\n");double num 2.5;double base 2;int i 3;while (i < 10){base base * i;num num 1 / base;printf("%d %…

杭电-pid1013-digital roots

杭电-pid1013-digital roots 九余数定理 #include using namespace std;int main() {string s;while (cin>>s){if (s[0] 0){break;}int sum 0, i;for (i 0; i < s.length(); i){sum sum s[i] - 0;}if (sum % 9 0){cout<<"9"<…
网站地图