捌玖网络工作室's Archiver

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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
/NJ-@M)x9z^g,] A b 它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
b#I/Z2R$^,L,t 可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
!L'I~n8N7H#Q@*^ 定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
m#p"W'_s3g7Y(x9@ 实际的名称与之相对应.
JboGT\0@)v ]~ 由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
w Aa F1h1Q0Y.vYe 然而,更多地自由,同样也加大操作上的难度.
(V*`|(]7{J 以下就对可变参数的几个方面作一定的介绍.H+CTt9?r
2f3s!o.@*Z
1)可变参数的存储形式.y2V g0A'e5l

/}I0?S.P&J*@ 大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
n?xSP,rb~1T/g{ 存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
y%|}5t^$m*d-] 在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈, \kh*p%o;w{p
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
$K4M4@kk*U 因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
I`%C].n U)d_ 栈区:4s/Pw%U%h4` ~
,N/}Y?Q!S_CN:u"a
|栈顶             低地址[ _3E ? y-MeN

@Pa1q%PC9?F |第一个固定参数var1
%X~.~.v? i;@ | |可变参数前的第一个固定参数var2"a oHPz*dPBO @
|可变参数的第一个参数xyE[mx2P H/m
|...
C6?E u&ge |可变参数的最后一个参数#S;g$E0u*C{w(Tu
|函数的倒数第二个固定参数var3
VW[Z*x:m!HL |函数的最后一个固定参数var4 y e#B'c-B3p
|...
(eL"nzG,b4m4@$I |函数的返回地址E"h^)ym8D4~}!A$~z}
|...
0[ s2lDG |栈底    高地址
0S8M8J{?M7ak k%O"@.o;u1IH4vFm
2)使用可变参数所用到头文件和相关宏说明8hVR5L9o;ZY-N
g2K\"J|t/uBF6R }
在此,以TC2.0编译器为参考对象来说明.
5a(Pqa'|%c9vF 可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
)dB9q*?)p 此文件为:
*W,F @m} C Y)R6si@ /* stdarg.hXc)yY$fw
H;~!Vgo g
Definitions for ACCESSing parameters in functions that accept
L:mA-[@%xb a variable number of arguments. ?l;b{|@p3F&j(o
_3N+g {7MR W
Copyright (c) Borland International 1987,19882Kb/ZH'c#uY7N
All Rights Reserved.~/|0w)St5N
*/
X#k/_J,t9^ #if __STDC__*~ @p4@/K!i+U5a`
#define _Cdecl7ld&O? Mqz
#else
}1^fg"fr #define _Cdecl cdecl
gm}a0C&{ #endif}4F p:X J|
9WRH`PT%G$h?I
#if !defined(__STDARG)
7HU%[*I1Y ? dCW@l #define __STDARG
#Z+n8J3R Ivm #V(Y]-P'AZ-\
typedef void *va_list;
;YH dY!Nm y
7E8I,~E h{5l #define va_start(ap, parmN) (ap = ...)'P9u a | Jg
#define va_arg(ap, type) (*((type *)(ap))++)7h;o$dN}1M` K b
#define va_end(ap)
&{_#w q j6]$fH~+n #define _va_ptr   (...)'u1O?pZNtp
#endif
4M%m^rk t2[]
w t@]x:WU"P,\ m y 以上为"STDARG.H"的内容.
;?a2c#Rdv 该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;_$DhcdM2J
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
H E2O+LI(o$p"A parmN为可变参数的前面一个固定参数. d-C5N0FbC3E;Q*R%z
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
7wI$t ZNg va_end(ap) 结束可变参数获取.N2PKc:q-Zs%a9u
RvQ0oN p'b9{l4I
3)可变参数的使用实例
W)u7l])A#[.{
'G$o:NAl"HI 实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.{7pg*YR*c,wU/i
2m3` C3@f0eY
#include<stdio.h>$SKS8d5V3?e
#include<conio.h>
fBn-PZp #include<stdarg.h>
K Rx4G*Ko.`c/D void tVarArg(int num,...);/*num为可变参数的个数*/
g5{ z F{|7t int main(void)
tZ Zi | \/c {
Q$u;a+u3a aR W clrscr();
7k USj|5wB{ \T9Y f*T,\ tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
+lU`kt&N tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");S8ziW/i)St2B@Y
getch();|&}*F$n? |_
return 0;
'dkM/g4p^9x }&r K]8h b9^ d3?3R{n
void tVarArg(int num,...) ?T!r"IS@k0yb
{-h/H2s+\Y8yo-r0Q(~Qq
va_list argp;  /*定义一个指向可变参数的变量*/
xgr)_&q r va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/!f$a#Lvl5u)Z
while(--num>=0)'|L1^R9nsOtH
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
(]-Y R?cn     并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/a#Oxb jp[
va_end(argp);  /*结束可变参数获取*/ I7A#_oo&X-p4dB\
return ;
3M0Z1j#T\j }
!qNq:{9pvU _+V
A Y4D:@AG k(aui 4)可变参数的使用需要注意的问题1q/@9B0Z&w;i:@
b&P2~uuz*?
1.每个函数的可变参数至多有一个.
i6^^4?&LV o 2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
F q*T r*g+} L 3.可变参数的个数不确定,完全由程序约定.
J;d|6J7Ca"B8b 4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
` cZL ^3khc6j 而printf()中不是实现了识别参数吗?那是因为函数 0o@$XG#}6["y8y
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
~NiZEs%? 的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 wW)I'K7GLf7E
过在自己的程序里作判断来实现的. (_2|I(sRJU ~*tk
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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


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