void vltest(int i, float k, ...){
va_list vl; //定义va_list变量vl,该变量是指向参数的指针
va_start(vl, k); // 参数一:va_list变量vl;参数二:va_list变量vl中最后一个固定参数
int j = va_arg(vl, int); // 参数一:va_list变量vl;参数二:可变参数的类型,返回值j即可变参数
double m = va_arg(vl, double); // 同上
unsigned long n = va_arg(vl, unsigned long); // 同上
va_end(vl); // 结束可变参数的获取
printf("i = %d, k = %.f, j = %d, m = %lf, n = %lu\r\n", i,k, j, m, n);
}
上述方法不能智能识别不同参数的个数和类型。
如果想实现智能识别可变参数,比如printf,需要在自己的程序中作特殊处理,示例如下:
#include <stdarg.h>
void my_printf(const char* fmt, ... )
{
va_list ap;
va_start(ap,fmt); /* 用最后一个具有参数的类型的参数去初始化ap */
for (;*fmt;++fmt)
{
/* 如果不是控制字符 */
if (*fmt!='%')
{
putchar(*fmt); /* 直接输出 */
continue;
}
/* 如果是控制字符,查看下一字符 */
++fmt;
if ('\0'==*fmt) /* 如果是结束符 */
{
assert(0); /* 这是一个错误 */
break;
}
switch (*fmt)
{
case '%': /* 连续2个'%'输出1个'%' */
putchar('%');
break;
case 'd': /* 按照int输出 */
{
/* 下一个参数是int,取出 */
int i = va_arg(ap,int);
printf("%d",i);
}
break;
case 'c': /* 按照字符输出 */
{
/** 但是,下一个参数是char吗*/
/* 可以这样取出吗? */
char c = va_arg(ap,char);
printf("%c",c);
}
break;
}
}
va_end(ap); /* 释放ap—— 必须! 见相关链接*/
}
在C语言中,调用一个不带原型声明的函数时:
调用者会对每个参数执行“默认实际参数提升(default argument promotions)”。
同时,对可变长参数列表超出最后一个有类型声明的形式参数之后的每一个实际参数,也将执行上述提升工作。
提升工作如下:
——float类型的实际参数将提升到double
——char、short和相应的signed、unsigned类型的实际参数提升到int
——如果int不能存储原值,则提升到unsigned int
然后,调用者将提升后的参数传递给被调用者。
因此,my_printf是绝对无法接收到上述类型的实际参数的。
上面的代码的38与39行,应该改为:
int c = va_arg(ap,int);
printf("%c",c);
同理, 如果需要使用short和float, 也应该这样:
short s = (short)va_arg(ap,int);
float f = (float)va_arg(ap,double);
总之,va_arg(ap,type)中的type绝对不能为以下类型:——char、signed char、unsigned char
——short、unsigned short
——signed short、short int、signed short int、unsigned short int
——float
注:部分编译器,如笔者所测试的xCode4.3,输入上述错误类型后有警告提示。
参考:
va_list(),va_start() ,va_end()