可变参数宏__VA_ARGS__
__VA_ARGS__是一个可变参数的宏,定义时宏定义中参数列表的最后一个参数为省略号,在实际使用时会发现有时会加##,有时又不加。
//最简单的定义
#define my_print1(...) printf(__VA_ARGS__)
//搭配va_list的format使用
#define my_print2(format, ...) printf(format, __VA_ARGS__)
#define my_print3(format, ...) printf(format, ##__VA_ARGS__)
__VA_ARGS__宏前面加上##的作用在于,当可变参数的个数为0时,这里printf参数列表中的的**##会把前面多余的","去掉**,否则会编译出错,建议使用后面这种,使得程序更加健壮。
va_list
用法
VA_LIST的用法:
(1)首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针;
(2)然后用VA_START宏初始化变量刚定义的VA_LIST变量;
(3)然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型(如果函数有多个可变参数的,依次调用VA_ARG获取各个参数);
(4)最后用VA_END宏结束可变参数的获取。
原理
可变参数是由宏实现的,但是由于硬件平台的不同,编译器的不同,宏的定义也不相同,下面是VC6.0中x86平台的定义:
typedef char *va_list;
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) ) //为了满足需要内存对齐的系统
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) //ap指向第一个变参的位置,即将第一个变参的地址赋予ap
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) /获取变参的具体内容,t为变参的类型,如有多个参数,则通过移动ap的指针来获得变参的地址,从而获得内容/
#define va_end(ap) ( ap = (va_list)0 ) //清空va_list,即结束变参的获取
C语言的函数形参是从右向左压入堆栈的,以保证栈顶是第一个参数,而且x86平台内存分配顺序是从高地址到低地址。因此似函数AVEInt(int var1,intvar2,…,int varN)内存分配大致上是这样的:(可变参数在中间)
va_list ap; 定义一个va_list变量ap
va_start(ap,v);执行ap = (va_list)&v + _INTSIZEOF(v),ap指向参数v之后的那个参数的地址,即ap指向第一个可变参数在堆栈的地址。
va_arg(ap,t) , ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )取出当前ap指针所指的值,并使ap指向下一个参数。
ap+= sizeof(t类型),让ap指向下一个参数的地址。然后返回ap-sizeof(t类型)的t类型指针,这正是第一个可变参数在堆栈里的地址。然后
用取得这个地址的内容。
va_end(ap); 清空va_list ap。
注意
使用VA_LIST应该注意的问题:
(1)因为va_start, va_arg,
va_end等定义成宏,所以它显得很愚蠢,可变参数的类型和个数完全在该函数中由程序代码控制,它并不能智能地识别不同参数的个数和类型.
也就是说,你想实现智能识别可变参数的话是要通过在自己的程序里作判断来实现的.
(2)另外有一个问题,因为编译器对可变参数的函数的原型检查不够严格,对编程查错不利.不利于我们写出高质量的代码。
(3)由于参数的地址用于VA_START宏,所以参数不能声明为寄存器变量,或作为函数或数组类型。
应用
输出abc123
#include <cstdio>
#include <stdarg.h>
#include <string.h>
#define log(format,...) write_log(format,__VA_ARGS__)
char *m_buf;
void write_log(char* format,...)
{
m_buf = new char[1000];
va_list valst;
//将传入的format参数赋值给valst,便于格式化输出
va_start(valst, format);
//调用vsnprintf拷贝进入缓冲区
int m = vsnprintf(m_buf, 1000, format, valst);
m_buf[m + 1] = '\0';
va_end(valst);
}
int main()
{
log("abc%d", 123);
printf("%s", m_buf);
}