加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

C/C++中va_list,va_start,va_arg,va_end等可变参数宏的使用

(2017-03-30 15:38:34)
分类: C/Cplusplus
之前在学习可变参数时,没有记笔记导致现在对可变参数记忆不清楚,所有又重新学习了可变参数。今天打算学习的资料记录下来,供以后便于学习和查找。

我在查找相关资料的时候,发现很多人对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)

注:以上信息借鉴互联网。


0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有