捌玖网络工作室's Archiver

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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
\ fR-I2Y d 它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
3TN:_ w|-Po6H"p+r 可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
\#U(x l!ku(v,d 定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有2a/gw@&r?
实际的名称与之相对应.+\.x6|m0Dgc/g(S
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.;r,kN oJ(saOh
然而,更多地自由,同样也加大操作上的难度.
7}TZUP 以下就对可变参数的几个方面作一定的介绍.
A~;lrs&LY,L&?
wu x9h e_%jP*i 1)可变参数的存储形式.
]fV5^'fN6@O s L8r4X o J$n)M5A7DvO
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
v}{;o,v l1sls 存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.5A{ u`)u8hn
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,;VFA(QF'Av
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
GB-F*nhI2p 因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
j CIp$gyLA7t 栈区:
Yn*^#} TFj3u'o O K"TdWvt
|栈顶             低地址&^-\Pp;A'Q

;Y BV3C5V1\?"@ |第一个固定参数var1m}-N'mW&y M
|可变参数前的第一个固定参数var2p k MIgYf
|可变参数的第一个参数
m"S`7s$m:T |...
+u;X]*xH{ |可变参数的最后一个参数U#SW8wS
|函数的倒数第二个固定参数var3
}&PN_,RW%n |函数的最后一个固定参数var4zr;F/@'Ls
|...cSvfY Y%@
|函数的返回地址
5}'d Lo$Z to |...
r8pk-CA J+Ne |栈底    高地址 Z8i3[8V!W{BN$M
"kv0U8oQEIS
2)使用可变参数所用到头文件和相关宏说明`!P-l:{H6wMm(T

.m|'YRT ? b 在此,以TC2.0编译器为参考对象来说明. g d N2y'Q1hV
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
)CFU xk7N2c}6W:x)_A 此文件为:F/q2bfo*u
/* stdarg.h
;[Q2{1Fm/i
#p)\wy$rhOp Definitions for ACCESSing parameters in functions that acceptv0TC9Q [+k8Uc
a variable number of arguments.
i2Yf0p.V6M,`*cA 1j#tNd[_:Gv
Copyright (c) Borland International 1987,1988Wv!d-y_
All Rights Reserved.
[E vk6pl4N$S9N3w */~cwC;e'o
#if __STDC__X M0I A.Z-e{]0g3Po c} \
#define _Cdecl
h Sp-p b jzA #else:e xT9aR C1K
#define _Cdecl cdecl
p y b Wod+E2Ket #endifTXQN]l^;C

$b2a1O8N J #if !defined(__STDARG)
.ne+[?J u #define __STDARGu's1W.Y'n*Ik

'd}P]%H typedef void *va_list;+~*pZT,sq5],l

j:_B cgl#C3B6j #define va_start(ap, parmN) (ap = ...))`XV.B4bT9]X-@%B;Z
#define va_arg(ap, type) (*((type *)(ap))++)
i#[3H:AN5Jc,{'] #define va_end(ap)
scJ4|9V8Z4S.C #define _va_ptr   (...)
%t V/L~&G ^'K[ #endif
mPqM(`r9H @ a-XY6nj,O
以上为"STDARG.H"的内容.2\bTo:M$O6C~
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
]8b(@h2ie#P va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
D R"G#Y{:a F1dg s%S parmN为可变参数的前面一个固定参数.Yk'Xj6a5n P
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.1l$o7l7k_)hb
va_end(ap) 结束可变参数获取.PJ4E6V Dw%Z;}

/iZ C;Ha+c^?-K 3)可变参数的使用实例
I2q kDTa%Nq
ea/Qd)v P 实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
Mp"B,I6jW0a*H] JD
Edm w4O%e'O`{ #include<stdio.h>W7H5^Cl7o j
#include<conio.h>.Tg'y*C W!]
#include<stdarg.h>
OT9g+KZ(w+y void tVarArg(int num,...);/*num为可变参数的个数*/
BY#m;K3}_H-R int main(void)![z/r Zn:\
{A6iR'Mz'v"T O9W
clrscr();
$|BC5r].vI MpT9B tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");r"R?7ri
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
KM2k@^3x;B*n*v getch();
1w*]b ^9W\iE@ return 0;&l4q)OF'oMbT
}Ct8d4Jxl2Eb
void tVarArg(int num,...)i?-lg1S,x:nvE O U
{
X#v/D UKv3sJ va_list argp;  /*定义一个指向可变参数的变量*/+k)Y1O"t%Jw;q+_(^
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/],WUSBWh)V
while(--num>=0)
Wa6?Z;O"h3Y?   printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
p~)wvr$f/n     并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/^+g4`_ h-TEE x@ ?f
va_end(argp);  /*结束可变参数获取*/Ax:K*xf4@U
return ;
2nSVz]+y9|@-@ }5~'m3savzv
F:t(p.~p"^J
4)可变参数的使用需要注意的问题+w _ U5e6o|
1F(_A'W Q
1.每个函数的可变参数至多有一个.
3tk"vc;Isg_'s 2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
znh:qz_os]$W&c 3.可变参数的个数不确定,完全由程序约定.
'v*].RW&}N L$O$H 4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
~ZztD!K 而printf()中不是实现了识别参数吗?那是因为函数
%|.b e)Hg8J9|X#e a printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 7I:G(nx m[TH;g2f
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 8h#H V#T|V#}| y1kK
过在自己的程序里作判断来实现的.
/nH6L;` P1d w 5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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


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