vprintf 与可变参数

来源:互联网 发布:淘宝上的老王手办 编辑:程序博客网 时间:2024/04/29 20:11

MSDN上说,类似printf的,有几对函数,一双一双的,功能是一样,但一个是接收可变参数,另一个是接收参数表的指针.
printf("%c %c %c/n",'a','b','c');  这个函数的输出是 a b c ,相信大家不会陌生了.
照说,vprintf就是接收参数表了,这个参数表,又是怎么一回事呢?我试了一下.
int vprintf( const char *format, va_list argptr );函数的定义是这样的
而 va_list 是 char* 类型.
想当然地,我写出了下面的代码.编译通过,运行出错.
char chArr[3] = {'a','b','c'}; //把参数放到数组中
vprintf("%c %c %c/n",chArr); //传参数的数组

我一直不知道我错在哪里.直到今天看到CSDN上有朋友问一个可变参的问题,我才知道了答案.

他问的是

 

这个函数的使用方法 int vsprintf( char *buffer, const char *format, va_list argptr );
void input(char *fmt,...)
{char temp[200];
 va_list args;
 va_start(args,fmt);
 vsprintf(temp, fmt, args);//这里第二个参数不是格式化声明么,怎么这里直接以一个变量名代替了,谁能解释下
 va_end(args); 
}

 

 

我在思考这个问题时才知道原来这里的参数数组va_list argptr,并不是简单的 char chArr[],
而是放在栈里面的一个一个的chArr数组的元素.
调用函数时,压在栈里的参数,是4字节对齐的.第一个参数就算只有1字节,也占了4字节.
而char chArr[]的数组,元素间是一个一个挨着放的.

然后我把代码改为如下
unsigned int ch[] = {'a','b','c'};
vprintf("%c %c %c/n",(char*)ch);
编译通过,运行成功了,结果与
printf("%c %c %c/n",'a','b','c');是一样的.
原来与 printf 对应的 vprintf 函数,他最后一个参数char*,并不是要取一个char[],
而是一个一个在栈里的4字节对齐的数据.

那些 va_xxx 又是怎么回事呢?
va_list args 其实是char* args,定义一个 char* 变量准备传给 v xxxx f 函数使用的.
va_start(args,fmt)
我们可以找到
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
的定义,要了解这个,首先要知道函数的参数到底是如何放置的

我写了一个小的测试代码
void ParamAddr(char* pCh,char ch,short s,int i)

 printf("pChAddr = %x/n",&pCh);
 printf("chAddr  = %x/n",&ch);
 printf("s Addr  = %x/n",&s);
 printf("i Addr  = %x/n",&i);
}
输出一看,就可以发现,&i 在最高地址.
然后地址一直减少,&pCh,在最高地址.

知道了参数是从左到右,从低到高排列,每参数最少占4字节之后.va_start就好理解了.
va_start(args,fmt) -->> args = (LPSTR)&fmt + _INTSIZEOF(fmt)[这个意思是4字节对齐的fmt大小]
就是args 等于 fmt 的地址 再加上 fmt 占用的大小,那么 args 就会指着 fmt 的下一个参数的地址了.
fmt的下一个参数的地址,指的正是可变参数在栈中的地址.这个地址传给
vprintf 的最后一个参数,就像我的这个代码一样.
unsigned int ch[] = {'a','b','c'};
vprintf("%c %c %c/n",(char*)ch);
就刚好符合 vprintf 的要求了.

 

 

 

 

 

 

 

 

 

 

原创粉丝点击