如何定义c 不定参数函数数

5843人阅读
c&c++(7)
#include &stdio.h&
#include &stdarg.h&&&&&&&&
//要包含这个头文件
void variable(int i, ...)
= <span style="color:#;
&&&&& va_list arg_&&& //第1步,定义这个指向参数列表的变量
va_start(arg_ptr, i);//第2步,把上面这个变量初始化.即让它指向参数列表
&&& while( j
!= -<span style="color:# )
&&&&&&& //第3步,获取arg_ptr指向的当前参数.这个参数的类型由va_arg的第2个参数指定
&&&&&&&&& j
= va_arg(arg_ptr,
&&&&&&&&& printf(&%d
&&&&& va_end(arg_ptr);&&& //第4步,做一些清理工作
&&&&& variable(<span style="color:#,
<span style="color:#, <span style="color:#,
<span style="color:#, <span style="color:#,
-<span style="color:#);
//////////////////////////
//////////////////////////
//////////////////////////
///////作为va_list,va_start的练习,可以学习一下使用_vsnprintf函数
#include &stdio.h&
#include &stdarg.h&
void formatoutput(char* format,
&&& char s[<span style="color:#];
&&&&& va_list arg_
&&&&& va_start(arg_ptr, format);
&&&&& _vsnprintf(s, sizeof(s)-<span style="color:#, format, arg_ptr);
&&&&& printf(&%s
void main()
&&&&& formatoutput(&%s%s&,
////////////////////
/////////////////////
参数个数不定的函数,最频繁使用的就是printf()与scanf()。其实,我们也可以自己实现这样的功能,首先看
一个例子:
#include &stdio.h&
#include &stdarg.h&
int Sum(int first, int second, ...)
&&&&&& int sum = 0, t =
&&&&&& va_
&&&&&& va_start(vl, first);
&&&&&& while (t != -1)
&&&&&&&& sum &#43;=
&&&&&&&& t = va_arg(vl, int);//将当前参数转换为int类型
&&&&& va_end(vl);
int main(int argc, char* argv[])
&&&&& printf(&The sum is %d\n&, Sum(30, 20, 10, -1));//-1是参数结束标志
&&&&&& return 0;
在上面的例子中,实现了一个参数个数不定的求int型和的函数Sum()。函数中一开始定义了一个va_list型变量
vl,该变量用来访问可变参数,实际上就是指针,接着使用va_start使vl指向第一个参数,然后再使用va_arg
来遍历每一个参数,va_arg返回参数列表中的当前参数并使vl指向参数列表中的下一个参数。最后通过va_end
把vl指针清为NULL。在这里,va_start,va_arg,va_end其实都是宏,那么这些宏又是如何实现他们的功能的
一个很显然的问题就是既然参数个数在函数调用之前没有确定,所以,在函数定义的时候没有办法像普通函数
那样使用确定的变量来接受传进来的参数,于是,问题的关键就是如何接收到这些不确定的参数了?首先,看
看函数调用时实际发生的情况,在函数调用的时候,使用栈传递参数,拿上例来说,如果调用Sum(30, 20, 10,
-1),那么参数入栈后的情形如下图所示(假定按照自右至左的顺序入栈):
既然参数在栈中的情形已经知道了,那么,如果使用指针(程序中的vl)指向第一个参数(va_start(vl,
first)),因为所有参数都是连续存放的,通过移动指针就可以访问到每一个参数了(va_arg(vl, int)),这
就是我在程序中使用到那几个宏的实现思想。
&&&&&&& 明白了以上原理之后,就可以完全不使用宏,自己实现这样的一个功能了,下面的程序是我按照上面的
思想改写的:
#include &stdio.h&
int Sum(int first, int second, ...)
int sum = 0, t =
char *//定义一个指针
vl = (char *)&//使指针指向第一个参数
while (*vl != -1)//-1是预先给定的结束符
&&&&&&& sum &#43;= *(int *)//类型转换
&&&&&&& vl &#43;= sizeof(int);//移动指针,使指针指向下一个参数
int main(int argc, char* argv[])
printf(&The sum is %d\n&, Sum(30, 20, 10, -1));//-1是参数结束标志
可以看出,通过使用指针的确实现了参数个数不定的函数了,但是程序中还有一个问题,那就是移动指针,在
程序中因为我使用的参数都是相同的int类型,所以可以事先知道访问下一个参数的时候应该移动sizeof(int)
个字节,但是,如果参数的类型不同呢?这的确是个比较麻烦的问题,因为不同的数据类型占用的字节数可能
是不一样的(如double型为8个字符,short int型为2个),所以很难事先确定应该移动多少个字节!但是办法
还是有的,这就是使用指针了,无论什么类型的指针,都是占用4个字节,所以,可以把所有的传人参数都设置
为指针,这样一来,就可以通过移动固定的4个字节来实现遍历可变参数的目的了,至于如果取得指针中的内容
并使用他们,当然也是无法预先得知的了,所以这大概也就是像printf(),scanf()之类的函数还需要一个&#26684;式
控制符的原因吧^_^!不过实现起来还是有不少麻烦,暂且盗用vprintf()来实现一个与printf()函数一样功能的
函数了,代码如下:
void myPrint(const char *frm, ...)
&&& va_start(vl, frm);
&&& vprintf(frm, vl);
&&& va_end(vl);
//////////////////////
////////////////////////
书上说,当无法列出传递函数的所有实参的类型和数目时,可用省略号指定参数表(...)
如:void foo(...);
void foo(parm_list,...);
void foo(...)
&&&& //...
调用:foo(a,b,c);
就是不懂,把a,b,c的&#20540;传进函数里面后,用什么变量来接收???如果不能接收,(...)岂不是没意义?
还有就是不明白
int printf(const char*...);
printf(&hello,&s\n&,userName);
这个c的输出函数是怎么用(...)实现的.
首先函数体中声明一个va_list,然后用va_start函数来获取参数列表中的参数,使用完毕后调用va_end()结束。像这段代码:
void TestFun(char* pszDest, int DestLen, const char* pszFormat, ...)
va_start(args, pszFormat);
_vsnprintf(pszDest, DestLen, pszFormat, args);
va_end(args);
///////////////////////////
一个简单的可变参数的C函数
&&&&& 先看例子程序。该函数至少有一个整数参数,其后占位符…,表示后面参数的个数不定。在这个例子里,所有的输入参数必须都是整数,函数的功能只是打印所有参数的&#20540;。函数代码如下:
//示例代码1:可变参数函数的使用
#include &stdio.h&
#include &stdarg.h&
void simple_va_fun(int start, ...)
&&&& va_list arg_
&&&& int nArgValue =
&&&& int nArgCout=0;&& //可变参数的数目
&&&& va_start(arg_ptr,start);&& //以固定参数的地址为起点确定变参的内存起始地址。
&&&&&&&& &#43;&#43;nArgC
&&&&&&&& printf(&the %d th arg: %d&,nArgCout,nArgValue); //输出各参数的&#20540;
&&&&&&&& nArgValue = va_arg(arg_ptr,int);&& //得到下一个可变参数的&#20540;
&&&& } while(nArgValue != -1);&&&&&&&&&&&&&&&
int main(int argc, char* argv[])
&&&& simple_va_fun(100,-1);
&&&& simple_va_fun(100,200,-1);
&&&& return 0;
下面解释一下这些代码。从这个函数的实现可以看到,我们使用可变参数应该有以下步骤:
⑴由于在程序中将用到以下这些宏:
void va_start( va_list arg_ptr, prev_param );
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );
va在这里是variable-argument(可变参数)的意思。
这些宏定义在stdarg.h中,所以用到可变参数的程序应该包含这个头文件。
⑵函数里首先定义一个va_list型的变量,这里是arg_ptr,这个变量是存储参数地址的指针.因为得到参数的地址之后,再结合参数的类型,才能得到参数的&#20540;。
⑶然后用va_start宏初始化⑵中定义的变量arg_ptr,这个宏的第二个参数是可变参数列表的前一个参数,即最后一个固定参数。
⑷然后依次用va_arg宏使arg_ptr返回可变参数的地址,得到这个地址之后,结合参数的类型,就可以得到参数的&#20540;。
⑸设定结束条件,这里的条件就是判断参数&#20540;是否为-1。注意被调的函数在调用时是不知道可变参数的正确数目的,程序员必须自己在代码中指明结束条件。至于为什么它不会知道参数的数目,在看完这几个宏的内部实现机制后,自然就会明白。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:141367次
积分:2212
积分:2212
排名:第14439名
原创:75篇
转载:27篇
评论:22条
(1)(1)(1)(1)(6)(11)(2)(1)(5)(1)(1)(3)(2)(2)(4)(4)(11)(8)(5)(10)(6)(10)(6)温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
/*va_ //定义一个指向参数列表的变量(一个...指针)va_start(vl,first_param); //把指向参数列表的变量初始化va_arg(vl,mode); //获取下一个参数,参数类型由第二个参数指定,第二个参数用于在va_arg内部进行尺寸计算,以便找到下一个参数va_end(vl); //结束*/#include &iostream&#include &cstdarg& //头文件包含:C++ &cstdarg&; C &stdarg.h&using namespace std;void variable(int i,...){&&&&& int j=0;&&&&& va_list arg_ptr; //定义一个指向参数列表的变量&&&&& va_start(arg_ptr,i); //把指向参数列表的变量初始化&&&&& while(j!=-1) //自定义的一个参数结束标志&&&&& {&&&&& &&&&&& j=va_arg(arg_ptr,int); //获取下一个参数&&&&& &&&&&& printf("%d ",j);&&&&& }&&&&& va_end(arg_ptr); //结束}int main(){&&&&& variable(3,3,4,5,6,-1);&&&&& return 0;}一、什么是可变参数我们在C语言编程中有时会遇到一些参数个数可变的函数,例如printf()函数,其函数原型为:int printf( const char* format, ...);它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的(用三个点"…"做参数占位符),实际调用时可以有以下的形式:printf("%d",i);printf("%s",s);printf("the number is %d ,string is:%s", i, s);&&& 以上这些东西已为大家所熟悉。但是究竟如何写可变参数的C函数以及这些可变参数的函数编译器是如何实现,这个问题却一直困扰了我好久。本文就这个问题进行一些探讨,希望能对大家有些帮助.二、可变参数在编译器中的处理我 们知道va_start,va_arg,va_end 是在stdarg.h中被定义成宏的, 由于1)硬件平台的不同
2)编译器的不同,所以定义的宏也有所不同,下面看一下VC++6.0中stdarg.h里的代码(文件的路径为VC安装目录下的\vc98
\include\stdarg.h)typedef char *&&&& va_#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )#define va_start(ap,v)&&&& ( ap = (va_list)&v + _INTSIZEOF(v) )#define va_arg(ap,t)&&&&&& ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )#define va_end(ap)&&&&&&&& ( ap = (va_list)0 )下面我们解释这些代码的含义:1、首先把va_list被定义成char*,这是因为在我们目前所用的PC机上,字符指针类型可以用来存储内存单元地址。而在有的机器上va_list是被定义成void*的2、定义_INTSIZEOF(n)主要是为了某些需要内存的对齐的系统.这个宏的目的是为了得到最后一个固定参数的实际内存大小。在我的机器上直接用sizeof运算符来代替,对程序的运行结构也没有影响。(后文将看到我自己的实现)。3、 va_start的定义为 &v+_INTSIZEOF(v)
,这里&v是最后一个固定参数的起始地址,再加上其实际占用大小后,就得到了第一个可变参数的起始内存地址。所以我们运行 va_start(ap, v)以后,ap指向第一个可变参数在的内存地址,有了这个地址,以后的事情就简单了。这里要知道两个事情:⑴在intel+windows的机器上,函数栈的方向是向下的,栈顶指针的内存地址低于栈底指针,所以先进栈的数据是存放在内存的高地址处。(2)在VC等绝大多数C编译器中,默认情况下,参数进栈的顺序是由右向左的,因此,参数进栈以后的内存模型如下图所示:最后一个固定参数的地址位于第一个可变参数之下,并且是连续存储的。|--------------------------||&&&& 最后一个可变参数&&& |&&&&& -&高内存地址处|--------------------------||--------------------------||&&&& 第N个可变参数&&&&&&& |&&&&&&& -&va_arg(arg_ptr,int)后arg_ptr所指的地方,|&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&|&&&&&&& 即第N个可变参数的地址。|--------------- |&&&& |--------------------------||&&&& 第一个可变参数&&&&&&&|&&&&&&& -&va_start(arg_ptr,start)后arg_ptr所指的地方|&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&|&&&&&&& 即第一个可变参数的地址|--------------- |&&&& |------------------------ --||&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&||&&&& 最后一个固定参数&&&&&|&&&&&& -& start的起始地址|-------------- -|&&&&&&&&& .................|-------------------------- ||&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&|& |--------------- |&&&& -& 低内存地址处(4) va_arg():有了va_start的良好基础,我们取得了第一个可变参数的地址,在va_arg()里的任务就是根据指定的参数类型取得本参数的值,并且把指针调到下一个参数的起始地址。因此,现在再来看va_arg()的实现就应该心中有数了:#define va_arg(ap,t)&&&&&& ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )这个宏做了两个事情,①用用户输入的类型名对参数地址进行强制类型转换,得到用户所需要的值②计算出本参数的实际大小,将指针调到本参数的结尾,也就是下一个参数的首地址,以便后续处理。(5)va_end 宏的解释:x86平台定义为ap=(char*)0;使ap不再指向堆栈,而是跟NULL一样.有些直接定义为((void*)0),这样编译器不会为 va_end产生代码,例如gcc在linux的x86平台就是这样定义的.
在这里大家要注意一个问题:由于参数的地址用于va_start宏,所以参数不能声明为寄存器变量或作为函数或数组类型. 关于va_start,
va_arg, va_end的描述就是这些了,我们要注意的 是不同的操作系统和硬件平台的定义有些不同,但原理却是相似的.三、可变参数在编程中要注意的问题因 为va_start, va_arg, va_end等定义成宏,所以它显得很愚蠢,
可变参数的类型和个数完全在该函数中由程序代码控制,它并不能智能地识别不同参数的个数和类型.
有人会问:那么printf中不是实现了智能识别参数吗?那是因为函数
printf是从固定参数format字符串来分析出参数的类型,再调用va_arg
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通过在自己的程序里作判断来实现的. 例如,在C的经典教材《the c
programming language》的7.3节中就给出了一个printf的可能实现方式,由于篇幅原因这里不再叙述。四、小结:1、标准C库的中的三个宏的作用只是用来确定可变参数列表中每个参数的内存地址,编译器是不知道参数的实际数目的。2、在实际应用的代码中,程序员必须自己考虑确定参数数目的办法,如⑴在固定参数中设标志-- printf函数就是用这个办法。后面也有例子。⑵在预先设定一个特殊的结束标记,就是说多输入一个可变参数,调用时要将最后一个可变参数的值设置成这个特殊的值,在函数体中根据这个值判断是否达到参数的结尾。本文前面的代码就是采用这个办法.无论采用哪种办法,程序员都应该在文档中告诉调用者自己的约定。3、实现可变参数的要点就是想办法取得每个参数的地址,取得地址的办法由以下几个因素决定:①函数栈的生长方向②参数的入栈顺序③CPU的对齐方式④内存地址的表达方式结合源代码,我们可以看出va_list的实现是由④决定的,_INTSIZEOF(n)的引入则是由③决定的,他和①②又一起决定了va_start的实现,最后va_end的存在则是良好编程风格的体现,将不再使用的指针设为NULL,这样可以防止以后的误操作。4、取得地址后,再结合参数的类型,程序员就可以正确的处理参数了。理解了以上要点,相信稍有经验的读者就可以写出适合于自己机器的实现来。
阅读(4396)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
loftPermalink:'',
id:'fks_095069',
blogTitle:'[转载]C++不定参数的用法',
blogAbstract:'C++不定参数的用法 (C类似)
/*va_ //定义一个指向参数列表的变量(一个...指针)va_start(vl,first_param); //把指向参数列表的变量初始化va_arg(vl,mode); //获取下一个参数,参数类型由第二个参数指定,第二个参数用于在va_arg内部进行尺寸计算,以便找到下一个参数va_end(vl); //结束*/',
blogTag:'',
blogUrl:'blog/static/',
isPublished:1,
istop:false,
modifyTime:0,
publishTime:3,
permalink:'blog/static/',
commentCount:1,
mainCommentCount:1,
recommendCount:0,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:false,
hostIntro:'',
hmcon:'1',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}posts - 96,&
comments - 59,&
trackbacks - 0
函数的代码是在编译时候生成的,对于带有不定参数个数(甚至对于每一个参数对应的类型也不也一样)的函数,编译器怎么在支持这样的函数。&
1 #include & stdio.h&2 #include & string.h&3 #include & stdarg.h&4 6 int t(...) 7 {8   return 0;9 }10 /* 函数原型声明,至少需要一个确定的参数,注意括号内的省略号 */11 int demo( char *, ...) ;12 13 14 void main( void )15 16 {17 demo("DEMO"," This"," is "," a ","demo!", "\0");18 }19 20 int demo( char *msg, ... )21 {22 va_ /* 定义保存函数参数的结构 */23 int argno = 0; /* 纪录参数个数 */24 char * /* 存放取出的字符串参数 */25 26 va_start( argp, msg );27 while (1)28 {29 para = va_arg( argp, char *);30 if ( strcmp( para, "\0" ) == 0 ) /* 采用空串指示参数输入结束 */31 break;32 33 printf("参数 #%d 是: %s\n", argno, para);34 argno++;35 }36 va_end( argp ); /* 将argp置为NULL */37 38 return 0;39 }
有意思是介绍几个宏
#define _INTSIZEOF(n)&& ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
这个是为了内存对齐,可以将n这个类型长度转换为sizeof(int)的倍数长度。 比如sizeof (n)=9 ,sizeof(int)=4,
那么 _INTSIZEOF(n)&= (&9 + 4 -1)&~(4 -1) = 12/3 = 4. 也就是将 9 转换为 12,为边界对齐加了一个3.
关一个这个的证明在这:
蛮经典的:
&//#define va_start _crt_va_start&//#define _crt_va_start(ap,v)& ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )&//#define _ADDRESSOF(v)&& ( &reinterpret_cast&const char &&(v) )
//#define _INTSIZEOF(n)&& ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
&//#define _crt_va_arg(ap,t)&&& ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
1.从va_start宏依赖第一个函数地址,得到下一个函数的地址。这也就是为什么不定参数的函数需要至少一个参数。
2.对于函数参数的个数的判读,需要自己。如果printf里面第一个参数定义可以得到后面的参数个数,或者最后一个参数要有定义。
阅读(...) 评论()Hi,欢迎来到中国嵌入式培训高端品牌 - 华清远见嵌入式学院,专注嵌入式工程师培养13年!
全国咨询热线:400-706-1880
当前位置: >
> 不定参函数
不定参函数
时间:作者:吕泽
有很多同学对我们经常使用的函数printf和scanf都不以为然,其实这两个函数在c语言当中还是比较典型的。一般我们编程的时候,函数中形式参数的数目通常是确定的,在调用时要依次给出与形式参数对应的实际参数。但在某些情况下我们希望函数的参数个数可以根据需要确定,因此c语言引入可变参数函数。典型的可变参数函数的例子有printf()、scanf()。
下面我们就说一下这些函数的实现。
这些不定参数函数在c语言中是通过堆栈的方式实现的,这样看来理论上好像参数的个数是没有限制的实际上由于内存或者栈的大小的限制,实际上参数的个数是有上限的,只是这个上限我们一般情况下不会达到。如果达到了也会出现错误。
在c语言中,不定参函数的具体实现主要通过c语言定义的几个函数宏来完成,具体内部空间如何分配,如何调用均在其中,这也为我们自己完成一些不定餐函数提供了便利条件。下面介绍下这些函数宏。
VA_LIST 是在C语言中解决变参问题的一组宏,所在头文件:#include &stdarg.h&
INTSIZEOF 宏,获取类型占用的空间长度,最小占用长度为int的整数倍:
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
VA_START宏,获取可变参数列表的第一个参数的地址(ap是类型为va_list的指针,v是可变参数最左边的参数):
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
VA_ARG宏,获取可变参数的当前参数,返回指定类型并将指针指向下一参数(t参数描述了当前参数的类型):
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
VA_END宏,清空va_list可变参数列表:
#define va_end(ap) ( ap = (va_list)0 )
具体用法:
(1)首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针;
(2)然后用VA_START宏初始化刚定义的VA_LIST变量;
(3)然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型(如果函数有多个可变参数的,依次调用VA_ARG获取各个参数);
(4)最后用VA_END宏结束可变参数的获取。
Ok下面举个例子来说明下:
void arg_test(int i, ...);
int main(int argc,char *argv[])
int int_size = _INTSIZEOF(int);
printf(&int_size=%d\n&, int_size);
arg_test(0, 4);
//arg_cnt(4,1,2,3,4);
void arg_test(int i, ...)
va_list arg_
va_start(arg_ptr, i);
printf(&&i = %p\n&, &i);//打印参数i在堆栈中的地址
printf(&arg_ptr = %p\n&, arg_ptr);//打印va_start之后arg_ptr地址
/*这时arg_ptr应该比参数i的地址高sizeof(int)个字节,即指向下一个参数的地址*/
j=*((int *)arg_ptr);
printf(&%d %d\n&, i, j);
j=va_arg(arg_ptr, int);
printf(&arg_ptr = %p\n&, arg_ptr);//打印va_arg后arg_ptr的地址
/*这时arg_ptr应该比参数i的地址高sizeof(int)个字节,即指向下一个参数的地址,如果已经是最后一个参数,arg_ptr会为NULL*/
va_end(arg_ptr);
printf(&%d %d\n&, i, j);
int int_size = _INTSIZEOF(int);得到int类型所占字节数
va_start(arg_ptr, i); 得到第一个可变参数地址
根据定义(va_list)&v得到起始参数的地址, 再加上_INTSIZEOF(v) ,就是其实参数下一个参数的地址,即第一个可变参数地址。
j=va_arg(arg_ptr, int); 得到第一个可变参数的值,并且arg_ptr指针上移一个_INTSIZEOF(int),即指向下一个可变参数的地址。
va_end(arg_ptr);置空arg_ptr,即arg_ptr=(void *)0;
在这里还要说一下,不定参数在c语言中的函数原型写法参数部分是&...&三个点。
最后附上一个我们自己写的简单的printf的代码:
#include &stdarg.h&
void my_printf(const char* fmt, ... )
va_start(ap,fmt); /* 用最后一个具有参数的类型的参数去初始化ap */
for (;*++fmt)
/* 如果不是控制字符 */
if (*fmt!=&#39;%&#39;)
putchar(*fmt); /* 直接输出 */
/* 如果是控制字符,查看下一字符 */
if (&#39;\0&#39;==*fmt) /* 如果是结束符 */
assert(0); /* 这是一个错误 */
switch (*fmt)
case &#39;%&#39;: /* 连续2个&#39;%&#39;输出1个&#39;%&#39; */
putchar(&#39;%&#39;);
case &#39;d&#39;: /* 按照int输出 */
/* 下一个参数是int,取出 */
int i = va_arg(ap,int);
printf(&%d&,i);
case &#39;c&#39;: /* 按照字符输出 */
/** 但是,下一个参数是char吗*/
/* 可以这样取出吗? */
char c = va_arg(ap,char);
printf(&%c&,c);
va_end(ap); /* 释放ap&& 必须! 见相关链接*/
看完后,你是不是自己也想试一下实现一个不定参函数呢。
评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)
学院最新动态}

我要回帖

更多关于 c语言不定参数函数 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信