va_list/va_start/va_arg/va_end深入分析
va_list/va_start/va_arg/va_end这几个宏,都是用于函数的可变参数的。
我们来看看在vs2008中,它们是怎么定义的:
1: ///stdarg.h
2: #define va_start _crt_va_start
3: #define va_arg _crt_va_arg
4: #define va_end _crt_va_end
5:
6: ///vadefs.h
7: #define _ADDRESSOF(v) ( &reinterpret_cast<const char &>(v) )
8: typedef char * va_list;
9: #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
10: #define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
11: #define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
12: #define _crt_va_end(ap) ( ap = (va_list)0 )
再看看各个宏的功能是什么?
- va_list用于声明一个变量,我们知道函数的可变参数列表其实就是一个字符串,所以va_list才被声明为字符型指针,这个类型用于声明一个指向参数列表的字符型指针变量,例如:va_list ap;//ap:arguement pointer
- va_start(ap,v),它的第一个参数是指向可变参数字符串的变量,第二个参数是可变参数函数的第一个参数,通常用于指定可变参数列表中参数的个数。
- va_arg(ap,t),它的第一个参数指向可变参数字符串的变量,第二个参数是可变参数的类型。
- va_end(ap) 用于将存放可变参数字符串的变量清空(赋值为NULL).
我们看一段具有可变参数列表的函数来求数组和的代码:
1: /*
2: *
3: *功能: 宏va_arg()用于给函数传递可变长度的参数列表。
4: *首先,必须调用va_start() 传递有效的参数列表va_list和函数强制的第一个参数。第一个参数代表将要传递的参数的个数。
5: *其次,调用va_arg()传递参数列表va_list 和将被返回的参数的类型。va_arg()的返回值是当前的参数。
6: *再次,对所有的参数重复调用va_arg()
7: *最后,调用va_end()传递va_list对完成后的清除是必须的。
8: *
9: *时间:2011年8月17日22:34:04
10: *作者:张超
11: *Email:uestczhangchao@gmail.com
12: *
13: */
14:
15:
16: #include "X:\编程练习\C-C++\global.h"
17:
18: #if va_arg==stdon
19: #include <stdio.h>
20: #include <stdarg.h>
21: #include <stdlib.h>
22:
23: //第一个参数指定了参数的个数
24: int sum(int number,...)
25: {
26: va_list vaptr;
27: int i;
28: int sum = 0;
29: va_start(vaptr,number);
30: for(i=0; i<number;i++)
31: {
32: sum += va_arg(vaptr,int);
33: }
34: va_end(vaptr);
35: return sum;
36: }
37:
38:
39: int main()
40: {
41: printf("%d\n",sum(4,4,3,2,1));
42: system("pause");
43: return 0;
44: }
45:
46: #endif
- va_start的功能是要把,ap指针指向可变参数的第一个参数位置处,
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
先取第一个参数的地址,在sum函数中就是取number的地址并且将其转化为char *的(因为char *的指针进行加减运算后,偏移的字节数才与加的数字相同, 如果为int *p,那么p+1实际上将p移动了4个字节),然后加上4(__INITSIZEOF(number)=(4+3)&~3),这样就将ap指向了可变参数字符串的第一个参数。
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
|
- va_arg是要从ap中取下一个参数。
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
对于这个宏,哥纠结了很久,最后终于搞清楚了,究其原因就是自己C语言功底不扎实,具体表现在没有搞清楚赋值表达式的值是怎么运作的。
我们看这个宏,首先是ap = ap + __INTSIZEOF(t)。注意到,此时ap已经被改变了,它已经指向了下一个参数,我们令x=ap + __INTSIZEOF(t);
那么括号内就变成了(x – __INTSIZEOF(t)),但是这里没有赋值运算符,所以ap的值没有发生变化,此时ap仍然指向的是当前参数的下一个参数的位置,
也就是说ap指向的位置比当前正在处理的位置超前了一个位置。
其实写成下面的形式就简单明了了:
#define va_arg(ap,t) (*(t *)((ap += _INTSIZEOF(t)), ap - _INTSIZEOF(t)) )
分析:为什么要将ap指向当前处理参数的下一个参数了?
经过上面的分析,我们知道va_start(ap,v)已经将ap指向了可变参数列表的第一个参数了,以后我们每一步操作都需要将ap移动到下一个
参数的位置,由于我们每次使用可变参数的顺序是:va_start(ap,v)—>va_arg(ap,t);这样我们在第一次去参数的时候,其实ap已经指向了
第二个参数开始的位置,所以我们用表达式的方式获得一个指向第一个参数的临时指针,这样我们就可以采用这种一致的方式来处理可变参数列表。
(感觉没表达的十分清楚,希望各位朋友纠正~~~~~~)。
下图是我的例子程序中去参数的情况(时间仓促,画得很丑,请原谅):
- va_end(ap) 将声明的ap指针置为空,因为指针使用后最后设置为空。
参考资料:
- http://topic.csdn.net/u/20110830/15/a3630fc4-3c5f-4a1e-bbee-949ba7b4cbe0.html
- http://topic.csdn.net/u/20070120/12/e8b7363b-6404-4d91-9307-01e5ed996f3d.html
va_list/va_start/va_arg/va_end深入分析相关推荐
- 变长参数va_list va_start va_arg va_end
对于int printf(const char *format, ...);这种变长参数,需要使用va_list va_start va_end va_arg来访问参数. 下面是一个tutorials ...
- C语言使用函数参数传递中的省略号:va_list, va_start, va_arg, va_end
首先要处理这种省略号的参数的话,需要包含头文件#include <stdarg.h>,然后利用下面的函数对"..."省略号变量进行处理. va_list arg; ty ...
- 如何获取函数的变长参数(va_list, va_start, va_arg, va_end)
最近在花时间研读C++. 函数这章讲到了函数的变长参数(ellipsis...),但是primer中讲得比较浅,提到了怎么声明怎么调用,但是没有写明在函数内部是如何获取变长的参数的. 1)省略号(el ...
- Va_list Va_start va_arg Va_end 的用法
首先我们先看看它的头文件是怎么描述的 stdarg.h #pragma once#ifndef _INC_STDARG #define _INC_STDARG#if !defined(_WIN32) ...
- 可变参数:va_list(),va_start(),va_arg(),va_end() 详细解析
目录 1.含义: 2.使用: 3.连续打印出自定义格式的文字: 1.含义: (1)va_list是C语言中的一个宏定义,用于表示一个变长参数列表.它是一个指向变长参数列表的指针,可以通过宏va_sta ...
- 可变参数的函数,va_start(), va_arg(), va_end()
头文件 stdarg:类型va_list:宏va_start:宏va_arg:宏va_end 1 #include <stdio.h>2 #include <stdarg.h> ...
- va_list/va_start/va_end的使用
va_list 键入以保存有关变量参数的信息 va_start 初始化变量参数列表 初始化ap以检索参数paramN后面的附加参数. 调用va_start的函数在返回之前也应调用va_end. 参数不 ...
- c语言va_start函数,va_start和va_end,以及c语言中的可变参数原理
FROM:http://www.cnblogs.com/hanyonglu/archive/2011/05/07/2039916.html 本文主要介绍va_start和va_end的使用及原理. 在 ...
- va_list、va_start和va_end使用
我们知道va_start,va_arg,va_end是在stdarg.h中被定义成宏的,由于1.硬件平台的不同 2.编译器的不同,所以定义的宏也有所不同. 在ANSI C中,这些宏的定义位于stdar ...
最新文章
- 如何打开MDI文档!
- 安卓图表引擎AChartEngine(一) - 简介
- 高级 Angular 组件模式 (3a)
- 如何做自己的服务监控?spring boot 1.x服务监控揭秘
- 数据驱动才是零售企业转型的核心!
- 学习有向图和无向图的强连通分量(基本概念+割点+点双联通分量+桥+边双连通分量+全套模板【Tarjan】)
- JAVA高并发秒杀系统构建之——高并发优化分析
- 外星人台式电脑_戴尔 XPS 和外星人大更新,一边是生产力,一边是游戏
- 产品总监的日常:管好团队必须先“正三观”
- gps84转换gcj02公式_【测绘基础】关于投影坐标和坐标转换,这几点你不得不知道!...
- 最小生成树之普里姆算法
- 听音乐学英语之- I Need to Wake Up 奥斯卡获奖单曲:关注全球变暖
- c语言程序设计cspn,Null-terminated wide strings
- 输入法或搜索类软件评价
- 2007年9月c语言真题及答案,1997年9月二级C语言笔试真题及答案.doc
- python3抓取-双色球开奖的所有历史数据-2003年始
- html progress标签的样式设置,progress后紧跟数字长度样式
- MONGODB的压力测试
- 解决python使用controlcan.dll接收报文每次只能处理一条报文信息问题
- 只会用Excel吗?这套全面的数据分析工具打包送你,拿走不谢
热门文章
- 图解iPhone开发新手教程
- D3D API - D3DXCreateRenderToSurface渲染到纹理
- Windows命令行下的进程管理
- 游戏的乐趣和任务设计
- 2021HDU多校6 - 7028 Decomposition(构造)
- 上海理工大学第二届“联想杯”全国程序设计邀请赛 - Experiment Class(几何+三分套三分)
- CodeForces - 1486F Pairs of Paths(树上计数+容斥)
- python导入同目录下的模块_如何从同一目录下的模块导入?
- 数据分析与挖掘实战-基于基站定位数据的商圈分析
- 排序算法-07归并排序(python实现)