valist用法(C++ 多个函数参数问题)
本文目录
- C++ 多个函数参数问题
- 前面声明过va_list ap;后面出现了va_start (ap, fmt),va_arg (ap, int)fmt 和 int 是什么参数啊
- 代码“va_start(ap,fmt)”是什么意思
C++ 多个函数参数问题
可变参数即表示参数个数可以变化,可多可少,也表示参数的类型也可以变化,可以是int,double还可以是char*,类,结构体等等。可变参数是实现printf(),sprintf()等函数的关键之处,也可以用可变参数来对任意数量的数据进行求和,求平均值带来方便(不然就用数组或每种写个重载)。在C#中有专门的关键字parame,但在C,C++并没有类似的语法,不过幸好提供这方面的处理函数,本文将重点介绍如何使用这些函数。 第一步 可变参数表示用三个点…来表示,查看printf()函数和scanf()函数的声明:int printf(const char *, ...);int scanf(const char *, ...);这三个点用在宏中就是变参宏(Variadic Macros),默认名称为__VA_ARGS__。如:#define WriteLine(...) { printf(__VA_ARGS__); putchar(’\n’);}再WriteLine("MoreWindows");考虑下printf()的返回值是表示输出的字节数。将上面宏改成:#define WriteLine (...) printf(__VA_ARGS__) + (putchar(’\n’) != EOF ? 1: 0);这样就可以得到WriteLine宏的返回值了,它将返回输出的字节数,包括最后的’\n’。如下例所示i和j都将输出12。 int i = WriteLine("MoreWindows"); WriteLine("%d", i); int j = printf("%s\n", "MoreWindows"); WriteLine("%d", j); 第二步 如何处理va_list类型函数内部对可变参数都用va_list及与它相关的三个宏来处理,这是实现变参参数的关键之处。在《stdarg.h》中可以找到va_list的定义:typedef char * va_list;再介绍与它关系密切的三个宏要介绍下:va_start(),va_end()和va_arg()。同样在《stdarg.h》中可以找到这三个宏的定义:#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )#define va_end(ap) ( ap = (va_list)0 )#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )其中用到的_INTSIZEOF宏定义如下:#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )来分析这四个宏:va_end(ap)这个最简单,就是将指针置成NULL。va_start(ap,v)中ap = (va_list)&v + _INTSIZEOF(v)先是取v的地址,再加上_INTSIZEOF(v)。_INTSIZEOF(v)就有点小复杂了。( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )全是位操作,看起来有点麻烦,其实不然,非常简单的,就是取整到sizeof(int)。比如sizeof(int)为4,1,2,3,4就取4,5,6,7,8就取8。对x向n取整用C语言的算术表达就是((x+n-1)/n)*n,当n为2的幂时可以将最后二步运算换成位操作——将最低 n - 1个二进制位清 0就可以了。va_arg(ap,t)就是从ap中取出类型为t的数据,并将指针相应后移。如va_arg(ap, int)就表示取出一个int数据并将指针向移四个字节。因此在函数中先用va_start()得到变参的起始地址,再用va_arg()一个一个取值,最后再用va_end()收尾就可以解析可变参数了。 第三步 vfprintf()函数和vsprintf()函数vfprintf()这个函数很重要,光从名字上看就知道它与经常使用的printf()函数有很大的关联。它有多个重载版本,这里讲解最常用的一种:函数原型int vfprintf( FILE *stream, const char *format, va_list argptr);第一个参数为一个FILE指针。FILE结构在C语言的读写文件必不可少。要对屏幕输出传入stdout。第二个参数指定输出的格式。第三个参数是va_list类型,这个少见,但其实就是一个char*表示可变参参数的起始地址。返回值:成功返回输出的字节数(不包括最后的’\0’),失败返回-1。vsprintf()与上面函数类似,就只列出函数原型了:int vsprintf( char *buffer, const char *format, va_list argptr);还有一个int _vscprintf(const char *format, va_list argptr );可以用来计算vsprintf()函数中的buffer字符串要多少字节的空间。代码范例下面就给出了自己实现的printf()函数(注1)与WriteLine()函数int Printf(char *pszFormat, ...) { va_list pArgList; va_start(pArgList, pszFormat); int nByteWrite = vfprintf(stdout, pszFormat, pArgList); va_end(pArgList); return nByteWrite;} int WriteLine(char *pszFormat, ...){ va_list pArgList; va_start(pArgList, pszFormat); int nByteWrite = vfprintf(stdout, pszFormat, pArgList); if (nByteWrite != -1) putchar(’\n’); //注2 va_end(pArgList); return (nByteWrite == -1 ? -1 : nByteWrite + 1);}调用与printf()函数相同。再给出一个用可变参数来求和,遗憾的在C,C++中无法确定传入的可变参数的个数(printf()中是通过扫描’%’个数来确实参数的个数的),因此要么就要指定个数,要么在参数的最后要设置哨兵数值:设置哨兵数值:const int GUARDNUMBER = 0; //哨兵标识//变参参数的个数无法确定,在printf()中是通过扫描’%’个数,在这通过设置哨兵标识来确定变参参数的终止int MySum(int i, ...){ int sum = i; va_list argptr; va_start(argptr, i); while ((i = va_arg(argptr, int)) != GUARDNUMBER) sum += i; va_end(argptr); return sum;}可以这样的调用: printf("%d\n", MySum(1, 3, 5, 7, 9, 0));但不可以直接传入一个0: printf("%d\n", MySum(0)); //error指定个数:int MySum(int nCount, ...){ if (nCount 《= 0) return 0; int sum = 0; va_list argptr; va_start(argptr, nCount); for (int i = 0; i 《 nCount; i++) sum += va_arg(argptr, int); va_end(argptr); return sum;}调用时第一个参数表示后面参数的个数如: printf("%d\n", MySum(5, 1, 3, 5, 7, 9)); printf("%d\n", MySum(0));代码所用的头文件:#include 《stdarg.h》#include 《stdio.h》 可变参数的使用方法远远不止上述几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃。
前面声明过va_list ap;后面出现了va_start (ap, fmt),va_arg (ap, int)fmt 和 int 是什么参数啊
给个例子自己看: 四个重要的宏: va_list va_start va_arg va_endva_list 定义了参数列表va_start 指定列表开始的参数va_arg 取出列表中的参数, 顺序为函数传递参数顺序(从左到右)va_end 参数列表结束举例:#include 《iostream》#include 《stdarg.h》using namespace std;int add(int totalnum...)//totalnum指定了参数的个数,...表示参数不定, 为定义此类函数必需{va_list intlist;//定义参数表 intlistva_start(intlist, totalnum);//指定开始参数为totalnumint totaladd = 0;for(int i=0;i《totalnum;i++){totaladd += va_arg(intlist,int);//取出参数类型为int的参数 你说的fmt是参数的类型如float等}va_end(intlist);//参数取完return totaladd;}void main(){cout《《add(5,1,2,3,4,5)《《endl;}答案为15
代码“va_start(ap,fmt)”是什么意思
VA_LIST 是在C语言中解决变参问题的一组宏,在《stdarg.h》头文件下。
VA_LIST的用法:
首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针,然后用VA_START宏初始化变量刚定义的VA_LIST变量,这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定的参数。
然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型。最后用VA_END宏结束可变参数的获取。然后你就可以在函数里使用第二个参数了。如果函数有多个可变参数的,依次调用VA_ARG获取各个参数。
VA_LIST在编译器中的处理:
在运行VA_START(ap,v)以后,ap指向第一个可变参数在堆栈的地址。VA_ARG()取得类型t的可变参数值,在这步操作中首先apt = sizeof(t类型),让ap指向下一个参数的地址。然后返回ap-sizeof(t类型)的t类型*指针,这正是 第一个可变参数在堆栈里的地址。然后用*取得这个地址的内容。
VA_END(),X86平台定义为ap = ((char*)0),使ap不再指向堆栈,而是跟NULL一样,有些直接定义为((void*)0),这样编译器不会为VA_END产生代码,例如gcc在Linux的X86平台就是这样定义的。
要注意的是:由于参数的地址用于VA_START宏,所以参数不能声明为寄存器变量,或作为函数或数组类型。
本文相关文章:
函数指针的循环调用(我的思路是 用循环调用函数,并用指针做形参,每次输出一个最大的值 结果没出来啊,希望大家帮忙找下错)
2024年10月17日 07:35
在线函数图像生成器(有什么软件可以画数学函数的图像电脑和手机都推荐一个)
2024年10月16日 04:00
编程代码大全c语言(用C语言编写程序,调用函数求一个圆柱体的表面积和体积)
2024年10月16日 02:20
fopen函数的用法 printf(怎么用C语言中的fopen函数打开bmp格式的图像文件)
2024年10月15日 19:50
matlab mesh函数用法(matlab怎么用mesh函数和ezmesh函数绘制函数z=x^2-3xy+y^2的曲面图啊)
2024年10月15日 14:45
eof函数返回值(当函数EOF()的返回值为真时,其表示文件的指针指向哪里)
2024年10月15日 03:35
python的replace函数怎么用(用Python写一个删除函数,可以删除指定的字符串或数字)
2024年10月14日 08:00
高中函数图像12种图像(高中数学函数的分类以及定义图像等是什么)
2024年10月12日 09:40
hlookup函数怎么用详细步骤(hlookup函数的使用方法)
2024年10月11日 17:55
c语言函数调用求和例子(求C语言定义一个函数求两个数的和,在主函数中调用)
2024年10月11日 14:55
sendmessage函数使用方法(vb中sendmessage函数的用法)
2024年10月8日 07:35
deleteobject函数(在函数中动态分配的内存怎么释放,没有把地址返回给调用它的函数)
2024年10月8日 04:35
imreconstruct函数(opencv中imreconstruct函数有什么作用)
2024年10月7日 13:50
std::function 函数指针(如何传递成员函数指针到std function)
2024年10月7日 08:15
clrscr函数功能(c语言中clrscr这个函数有什么用感觉有没有它都不影响啊)
2024年10月4日 18:50
matlab交流网站(MATLAB2015中的simulink建模后怎么得到波特图,能不能得到传递函数)
2024年9月30日 22:30
更多文章:
retaliation(vengeance和retaliation区别)
2024年7月5日 06:02
php switch(php.switch与for有什么区别)
2024年7月23日 22:22
轮询接口cleartimeout失效(调用接口出现异常是怎么回事)
2024年7月12日 08:15
在from子句中可以出现(在查询的from子句中实现表与表之间的连接有哪几种方式)
2024年9月29日 01:10
layuiadmin 破解版(layuiadmin哪一版本带树)
2024年7月13日 03:52
微服务架构和微信小程序的区别(微信公众号和微信小程序有什么区别)
2024年7月21日 03:34
12333微信公众号为什么显示数据加载中(微信公众号办理社保卡上传证件显示一直在加载中什么原因)
2024年8月31日 18:25