捌玖网络工作室's Archiver

zw2004 发表于 2008-1-21 19:42

函数的可变参数详谈

可变参数的英文表示为:variable argument.O~(u WjP9^;~
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
&Z'sP sy$Y9K&Z 可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
cq:U6QmO5c7hM8W 定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
_ |@ VWlWrm 实际的名称与之相对应.
} n;}7KV 由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
nz8_?+H 然而,更多地自由,同样也加大操作上的难度.NzXt2Yy!}/nF
以下就对可变参数的几个方面作一定的介绍./YFa J;[3{H7e

lf-_]?!T"_(}z3I 1)可变参数的存储形式.8I$Z.@#s?y X3Eh
1To"k$u p2^5hQ
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,}ja2L(^
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.%}{ v HM tI
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,-o4q}2^0I#N9U#oT$d
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.C(_H$vr'P
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):&EwMnc^6i4Q
栈区:
qmTev?Vg ~1S D]$~I:~lR
|栈顶             低地址
,d"EP!h3Q\Vtq $~_(Yc[x%j ]
|第一个固定参数var1
\o ^,fH8B2J-^ |可变参数前的第一个固定参数var2 q'h p5lAY
|可变参数的第一个参数
E5Zpe5q'c |...#l,QQ&h6c%j:p |
|可变参数的最后一个参数
GbJ.IR f |函数的倒数第二个固定参数var3
!h(| u rkjZ |函数的最后一个固定参数var46h O#_ ~&v1XV;b
|...
b$ad-o0z8} k` |函数的返回地址+B&W h8iu"m
|...
/Y:wkSQ |栈底    高地址
+_V{^Lst
&|1[ Eg(gX p[w 2)使用可变参数所用到头文件和相关宏说明
x q8\ Frcq mvaW(P.j]#F
在此,以TC2.0编译器为参考对象来说明.^Tm"TD0B
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.G0Z9Mi7t8u
此文件为: O3j3v [G'v-{ J
/* stdarg.h
@i1Y;[H!j `'O(n
~'DC5a$U v,Z1S%N Definitions for ACCESSing parameters in functions that acceptm w ~ pT B7A"u0L
a variable number of arguments.
*\k"WP5J4d.[S P{ F8aU;nW;b H(JB'^G
Copyright (c) Borland International 1987,1988
x%z,y?)Y`E$p Ps All Rights Reserved.
j`&S3\+^ wE */
+H,XPJ9~ b #if __STDC__dH@)n}
#define _Cdecl
(D2X9Us)D!BQQ1w"q_1i #else8M&w~1`{
#define _Cdecl cdecl
'|*I,XSr7s #endif\dwNV+z;@*I
6I6y+@y.\`&Y1ju
#if !defined(__STDARG)1U5oM7R7zj+o.r#|PIm5BA
#define __STDARG
'D*K#Y$ZF|$yX+VM8]*e
#H(Rs-w!An(J M.TG typedef void *va_list;1C%kEB/i aB@t*Z
JiA0UXc
#define va_start(ap, parmN) (ap = ...)
9w7NlB3V-y #define va_arg(ap, type) (*((type *)(ap))++)
7t&a i g E #define va_end(ap)8]*]+DA-}M^,amj
#define _va_ptr   (...)
i1OO'q^.H5` Do] #endif
m4VI{@ [+][ Rf fBS,@
以上为"STDARG.H"的内容.mo"@0?8[.G
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
i[Bx-_sQ va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,] RVC|$m4cik)V
parmN为可变参数的前面一个固定参数.2ex*` V-@]
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
\!q8TJseo&P/BF va_end(ap) 结束可变参数获取.
'X5C2QHvHF
t2a'G Cjn0[{ 3)可变参数的使用实例
A:X L8w9ct^9n
(SwXFw?[D 实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.Y:x(c8ay6gdRn0P

u&C;j0LxCV\ #include<stdio.h>
J3b0I!a;{)[+s9V-U(m1` #include<conio.h>
9H%a"~#vc} y #include<stdarg.h>
Tk4H/d'|:r;bX void tVarArg(int num,...);/*num为可变参数的个数*/
6xl.P_I q int main(void)
o4pT0jg:VrD {
UZxo3B l clrscr();
Asc {+u1k X7A/Q T tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");3xn.ME/RB,JK
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
|4S(SP)]+N.] getch();by9l s8P%x.zj?
return 0;?sI-r;p
}2l d4U~QM%y
void tVarArg(int num,...)s&x)S&a-}0m
{
q e1Tq)LB va_list argp;  /*定义一个指向可变参数的变量*/``4h\GA&hMP1?"e:WC
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/&Eg1E!G+M.E ]j
while(--num>=0) m6RJj'S!EPG
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,)_x P0b1WF
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
8ESE6xQ va_end(argp);  /*结束可变参数获取*/MX$ZU+B]
return ;5yFd+~X&n(F8` la
}I,x)K.|I~$E4x!g&br

&o-GzRNn7p(_ 4)可变参数的使用需要注意的问题`A$@j N(?&b/XO
t }7]w)S+^y
1.每个函数的可变参数至多有一个.8lJvy|g
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.l%? _8g4}2Fb
3.可变参数的个数不确定,完全由程序约定.
.[ j {Yhx6S^I 4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.} T [,u'e
而printf()中不是实现了识别参数吗?那是因为函数 D/X{ {`d7f
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
/d c?/kkaV3h7p@;g2J 的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 ,er)}U2m |`-p
过在自己的程序里作判断来实现的. *lK${U1Z,Z i#r"J
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

页: [1]
【捌玖网络】已经运行:


Powered by Discuz! Archiver 7.2  © 2001-2009 Comsenz Inc.