之前在学习可变参数时,没有记笔记导致现在对可变参数记忆不清楚,所有又重新学习了可变参数。今天打算学习的资料记录下来,供以后便于学习和查找。
我在查找相关资料的时候,发现很多人对va_list,va_start,va_arg,va_end学习时,都是先从它们本身讲起,即就是从宏的角度讲起。我感觉有时候对于编程,为了可以先会使用然后再去了解它的本质,当然这也和个人习惯有关,所以我先从使用的角度讲解,最后在讲解他们的本质。
1.
va_list,va_start,va_arg,va_end宏的使用
va_list的使用方法:
a)
首先在函数中定义一个具有va_list型的变量,这个变量是指向参数的指针。
b)
然后用va_start宏初始化变量刚定义的va_list变量,使其指向第一个可变参数的地址。
c)
然后va_arg返回可变参数,va_arg的第二个参数是你要返回的参数的类型(如果多个可变参数,依次调用va_arg获取各个参数)。
d)
最后使用va_end宏结束可变参数的获取。
在使用va_list是应该注意一下问题:
1)
可变参数的类型和个数完全由代码控制,它并不能智能地识别不同参数的个数和类型。
2)
如果我们不需要一一详解每个参数,只需要将可变列表拷贝到某个缓冲区,可以用vsprintf函数。
3)
因为编译器对可变参数的函数原型检查不够严格,对编程查错不利,不利于我们写出高质量的代码。
小结:
可变参数的函数原理其实很简单,而VA系列是以宏定义来定义的,实现跟堆栈有关。我们写一个可变参数的c函数时,有利有弊,所以在不必要的场合,我们无需用到可变参数,如果在C++里,我们应该使用多态来实现可变参数的功能,尽量避免使用C语言的方式来实现。
使用这些宏我们需要引用头文件#include
"stdarg.h" 。
我们来看一下代码如何书写:
#include
#include
int sum(char* msg, ...);
void print_vsprintf(char* dest, char* format, ...);
int main()
{
int total = 0;
total = sum("hello world", 1, 2, 3, 4, 5, 6, 7, 8, 9,
10);
std::cout << "total = " << total <<
std::endl;
char dest[256];
print_vsprintf(dest, "%My name is %s and I am %d
years old.", "Ben", 24);
std::cout << dest << std::endl;
return 0;
}
int sum(char* msg, ...)
{
va_list vaList;
va_start(vaList, msg);//第一个参数指向可变列表的地址,地址自动增加,第二个参数位固定值
std::cout << msg << std::endl;
int sumNum = 0;
int step;
while ( 0 != (step = va_arg(vaList,
int)))//va_arg第一个参数是可变参数的地址,第二个参数是传入参数的类型,返回值就是va_list中接着的地址值,类型和va_arg的第二个参数一样
{
//不等于0表示,va_list中还有参数可取
sumNum += step;
}
va_end(vaList);//结束可变参数列表
//std::cout << "列表的和为:" << sumNum <<
std::endl;
return sumNum;
}
void print_vsprintf(char dest[], char* format, ...)
{
va_list vaList;
va_start(vaList, format);
vsprintf(dest, format, vaList);
va_end(vaList);
}
运行结果:
hello world
total = 55
My name is Ben and I am 24 years old.
请按任意键继续. . .
2.
va_list,va_start,va_arg,va_end宏的定义及本质
a)
va_list型变量:
#ifdef
_M_ALPHA
typedef
struct{
char* a0;
int offset;
}va_list;
#else
typedef
char* va_list;#endif
b) _INTSIZEOF宏,获取类型占用的空间长度,最小占用长度为int的整数倍:
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1))
|------------------------------------------------|
高地址
|-------------函数返回地址-----------------------|
|------------.........................------------------|
|------------------------------------------------|<--va_arg后ap指向
|
第n个参数(第一个可变参数)
|
|------------------------------------------------|<--va_start后ap指向
|
第n-1个参数(最后一个固定参数)
|
|------------------------------------------------|<--&v 低地址
c)
VA_START宏,获取可变参数列表的第一个参数的地址(ap是类型为va_list的指针,v是可变参数最左边的参数,亦即最后一个固定参数):
#define va_start(ap,v) (ap=(va_list)&v+_INTSIZEOF(v))
4) VA_ARG宏,获取可变参数的当前参数,返回指定类型并将指针指向下一参数(t参数描述了当前参数的类型):
#define va_arg(ap,t) (*(t*)((ap+=_INTSIZEOF(t))-_INTSIZEOF(t)))
5) VA_END宏,清空va_list可变参数列表:
#define va_end(ap) (ap=(va_list)0)
注:以上信息借鉴互联网。
加载中,请稍候......