首先:来看一下stdio.h中的printf函数的声明:
int __cdecl printf(const char *, ...);
返回值是int,dll的调用方式是__cdecl,主要看一下参数:第一个是输出格式,后面的是...,就是说后面的参数的个数和类型不定.这是一个典型的函数不定参数的例子,那么是怎么实现的呢.
实现原理和理论依据:
函数参数传递的时候,参数是线性的存储在内存中的,因此,如果知道参数存放的起始位置和结束位置,和参数的类型,那么就可以得到需要的所有参数.
关于不定参数头文件stdarg.h中的几个宏定义(每一个颜色板块为一个宏定义及其解释):
va_list:
#ifdef _M_ALPHA
typedef struct {
char *a0; /* pointer to first homed integer argument */
int offset; /* byte offset of next parameter */
} va_list;
#else
typedef char * va_list;
#endif
其中a0或者va_list是第一个参数的地址,offset是从第一个参数到第二个参数的内存地址偏移量,由参数的类型占用内存的空间决定.
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
得到参数n的尺寸,相当与sizeof(n),为了兼容性写成了如上的表达式.
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
其中ap是一个va_list型的参数列表指针变量,v是参数表中的第一个参数的名字.作用是初始化参数表,并让参数表指针ap指向参数列表的第二个参数开始地址(根据以上宏定义的表达式得知).
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
根据参数类型移动参数表指针ap,使之返回下一个参数的值,参数的类型由第二个参数t指定(分析以上宏定义表达式).
#define va_end(ap) ( ap = (va_list)0 )
结束参数传递,做清理工作,即让参数表指针清空.
===================================================
下面给出几个实例代码:
/*
va_list vl; //定义一个指向参数列表的变量(一个...指针)
va_start(vl,first_param); //把指向参数列表的变量初始化
va_arg(vl,mode); //获取下一个参数,参数类型由第二个参数指定,第二个参数用于在va_arg内部进行尺寸计算,以便找到下一个参数
va_end(vl); //结束
*/
#include <iostream>
#include <cstdarg> //头文件包含:C++ <cstdarg>; C <stdarg.h>
using namespace std;
void variable(int i,...)
{
int j=0;
va_list arg_ptr; //定义一个指向参数列表的变量
va_start(arg_ptr,i); //把指向参数列表的变量初始化
while(j!=-1) //自定义的一个参数结束标志
{
j=va_arg(arg_ptr,int); //获取下一个参数
printf("%d ",j);
}
va_end(arg_ptr); //结束
}
int main()
{
variable(3,3,4,5,6,-1);
return 0;
}
/*
_vsnprintf();
*/
#include <iostream>
#include <cstdarg>
using namespace std;
void formatoutput(char * format,...)
{
char s[100];
va_list arg_ptr;
va_start(arg_ptr,format);
_vsnprintf(s,strlen(s),format,arg_ptr);
printf("%s",s);
}
int main()
{
formatoutput("%s%s","d","g");
return 0;
}
/*
不定参数用于多数据求和示例
*/
#include <iostream>
#include <cstdarg>
using namespace std;
int sum(int first,int second,...)
{
va_list vlist;
va_start(vlist,first);
int t=first;
int ans=0;
while(t)
{
ans+=t;
t=va_arg(vlist,int);
}
va_end(vlist);
return ans;
}
int main()
{
cout<<sum(1,2,3,0)<<endl;
return 0;
}
#include <iostream>
#include <cstdarg>
using namespace std;
void myprint(const char * format,...)
{
va_list vlist;
va_start(vlist,format);
int pos;
for(pos=0;pos<strlen(format)-1;pos++)
{
if(format[pos]=='%')
{
switch(format[pos+1])
{
case 'd':
cout<<va_arg(vlist,int);
break;
case 's':
cout<<va_arg(vlist,char *);
break;
default:
break;
}
pos++;
}
else
putchar(format[pos]);
}
va_end(vlist);
}
int main()
{
myprint("%s : %d%sI think it will be %s%s","This is just for test int and char * !",1,"/n","success!","/n");
return 0;
}
#include <iostream>
#include <cstdarg>
using namespace std;
/*需要按照类型进行偏移寻找下一个变量的地址,否则出错*/
void test(int first,char secend,double third,int fouth,...)
{
va_list vlist;
va_start(vlist,first);
cout<<va_arg(vlist,char)<<endl;
cout<<va_arg(vlist,double)<<endl;
cout<<va_arg(vlist,int)<<endl;
va_end(vlist);
}
int main()
{
test(1,'a',1.2,0);
return 0;
}
如果文章有错误的地方,请指出。
C++编程语言提供了很多手段用于创建和使用一个接受不定数目参数的函数,最典型的例子就是 printf()函数:
int printf(char *format, …); //省略号表示参数数目不定
使用这个函数十分的简单:
printf(“The sum of %d and %d is %d/n“, a, b, a+b);
创建这样的不定参数函数须依赖于一系列预先定义的宏。
背景:函数参数传递时,存储形式属于线形,根据参数顺序在内存中顺序存放,只要找到开始位置和结束位置,就可以找回所有参数。
方法:
首先,定义下面4个宏, 定义在stdarg.h文件中
#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(n) 找回参数占用字节数,( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) 相当于sizeof(n), 只是为了兼容非32位操作系统或其他非Windows平台而设置;
va_start(ap,v) 找回参数起始位置,
va_arg(ap,t) 找到下一参数
va_end(ap) 结束,一般可以不执行这一句
其次,不定参数中需要指定有结束标示符或者统计参数个数的计数器,例如,int printf(char *format, …),使用时,printf("I am %d", 24); 标示符就是"%d", 即在format中遍历,有%d,就将整形转为数字字符串,%s,字符字符串等等
实例:
求正整数平均值,-1就是自定义的结束标示符
#include "stdafx.h"
#include "iostream"
using namespace std;
typedef char * va_list;
/* A guess at the proper definitions for other platforms */
#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 )
int average1(int first,...);
int average2(int count, int first,...);
int _tmain(int argc, _TCHAR* argv[])
{
cout<<average1(1, 2, 3, 4, 5, -1)<<endl;
cout<<average2(5, 1, 2, 3, 4, 5)<<endl;
return 0;
}
int average1( int first, ... )
{
int count = 0, sum = 0;
int var = first;
va_list list;
va_start(list, first); /* Initialize variable arguments. */
while( var!=-1 )
{
sum += var;
count++;
var = va_arg( list, int);
}
va_end( list );/* Reset variable arguments. */
return( sum ? (sum / count) : 0 );
}
int average2(int count, int first, ... )
{
int sum = 0, var = first;
va_list list;
va_start( list, first );/* Initialize variable arguments. */
for(int k=0; k<count; k++)
{
sum += var;
var = va_arg( list, int);
}
va_end( list ); /* Reset variable arguments. */
return( sum ? (sum / count) : 0 );
}