printf(*format,…),其中的…就代表可变参数列表
需要包含的函数库
void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);
void va_copy(va_list dest, va_list src);
其中:
va_list是用于存放参数列表的数据结构。
va_start函数根据初始化last来初始化参数列表。
va_arg函数用于从参数列表中取出一个参数,参数类型由type指定。
va_copy函数用于复制参数列表。
va_end函数执行清理参数列表的工作。
注:va_start要与va_end配对使用;
上述函数通常用宏来实现,例如标准ANSI形式下,这些宏的定义是:
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) )
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap) ( ap = (va_list)0 )
使用宏_INTSIZEOF是为了按照整数字节对齐指针,因为c调用协议下面,参数入栈都是整数字节(指针或者值)。
以上内容引用自C语言可变参数函数详解示例
关于宏_INTSIZEOF,我们不妨举个例子(以32位系统为例):
int a=8;
_INTSIZEOF(a)=((sizeof(a)+sizeof(int)-1)&~(sizeof(int)-1))
=((sizeof(int)+sizeof(int)-1)&~(sizeof(int)-1))
=(4+4-1)&~(4-1)
=0x04&0xffff fffc
=0x04
对于不同的类型如下:
_INTSIZEOF(char)=((sizeof(char)+sizeof(int)-1)&~(sizeof(int)-1))
=(2+4-1)&~(4-1)
=0x05&0xffff fffc
=0x04
_INTSIZEOF(float)=((sizeof(float)+sizeof(int)-1)&~(sizeof(int)-1))
=(4+4-1)&~(4-1)
=0x04&0xffff fffc
=0x04
_INTSIZEOF(double)=((sizeof(double)+sizeof(int)-1)&~(sizeof(int)-1))
=(8+4-1)&~(4-1)
=0x0b&0xffff fffc
=0x08
用上可看出,不足整数字节的参数类型将会被转化成整数字节(char型转化为int型(低精度转高精度,数据不会丢失),flaot型转换为int(高精度转低精度,数据丢失)),具体问题查看如下:
同类型参数列表用法如下:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#define MAX_ARGS 4
void print0(void){
printf("print0\n");
}
void print1(int a){
printf("print1 %d\n",a);
}
void print2(int a, int b){
printf("print2 %d %d\n",a,b);
}
void print3(int a, int b, int c){
printf("print3 %d %d %d\n",a,b,c);
}
void print4(int a, int b,int c,int d){
printf("print4 %d %d %d %d\n", a, b, c, d);
}
void print(int num,...){
int i = 0;
int result = 0;
int args[MAX_ARGS] = { 0 };
int size = num > MAX_ARGS ? MAX_ARGS : num;
va_list v;
va_start(v, num);
for (i = 0; i < size; i++){
args[i] = va_arg(v, int);
}
switch (size){
case 1:
print1(args[0]);
break;
case 2:
print2(args[0], args[1]);
break;
case 3:
print3(args[0], args[1], args[2]);
break;
case 4:
print4(args[0], args[1], args[2],args[3]);
break;
default:
print0();
break;
}
va_end(v);
}
void main(){
print(0);
print(1,1);
print(2, 1, 2);
print(3, 1, 2, 3);
print(4, 1, 2, 3, 4);
system("pause");
}
运行结果:
同类型的参数列表,起始就是采用指针对数组的操作类似,参数列表的内存分布如下:
num(offset:4Bytes) |
---|
arg1(offset:4Bytes) |
arg2(offset:4Bytes) |
arg3(offset:4Bytes) |
arg4(offset:4Bytes) |
则取参数列表的步骤如下:
va_list v;
//step1:pa指针指向num表,并跳过num头
va_start(v,num);
//step2:提取argi参数
for(int i=0;i<num;i++){
args[i]=va_arg(v,int);
}
//step3:根据输入参数个数,分别执行不同的函数
switch(num){
case 1:xxx1(args[0]);break;
case 2:xxx2(args[0],args[1]);break;
case 3:xxx3(args[0],args[1],args[2]);break;
...
}
//step4:清空va_list指针
va_end(v);
当然了,以上只是一般形式,可参考printf(format*,…)形式,传入字符串,完成不同类型参数的传递;
不同类型参数列表用法如下:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
void print1(char* a){
printf("print1 %s\n",a);
}
void print2(char* a, float b){
printf("print2 %s %f\n",a,b);
}
void print(int num,...){
int i = 0;
int result = 0;
char* name = NULL;
double arg2 ;
va_list v;
va_start(v, num);
if (num == 1){
name = va_arg(v, char*);
print1(name);
}
else{
name = va_arg(v, char*);
arg2 = va_arg(v, double);//关于arg2参数必须为double,否则程序出错
print2(name, arg2);
}
va_end(v);
}
void main(){
print(1, "Bob");
print(2, "Lily", 13.02);
system("pause");
}