捌玖网络工作室's Archiver

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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
f]Cw3kgJ 它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
j#zC+hM5] B 可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
S-Hl u+^-R 定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有FeNDE+w9b"?6j
实际的名称与之相对应.
CAo'DP2},BO,G 由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
}rf#Awe!xsV 然而,更多地自由,同样也加大操作上的难度.(bl*@9{%}Q8^{*]:U
以下就对可变参数的几个方面作一定的介绍.zIwl/Z)GB
_m @/i:cn[y Fs
1)可变参数的存储形式.b ~)E\r+_
}sw ai;\cyeds
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,c-Z;@d2?\ EI
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.V2g+z7Lb%?Nkw5W
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
C6ZL;Q\3_Me[ 这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
?i7f]rTP 因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
`5}&y*NZ-twO Gz]%Y*? 栈区:
?xF0Y%H.g:ObN 9@T-z.G'x#Ah+devO
|栈顶             低地址
p_/M,K;m'f;N(BdF^2b 0H:ePRe!q
|第一个固定参数var1uH#| g ^
|可变参数前的第一个固定参数var2e:m&f(?Z4z$K,^j
|可变参数的第一个参数
]C.D m'i2\6Q-O1l*| |...
}rW[m0|2mn4o |可变参数的最后一个参数U hV t2i9uL@
|函数的倒数第二个固定参数var3
Um3]!I+iKq5WI |函数的最后一个固定参数var4
[#]pc&E5K |...
!].xm#D)LQ z!t |函数的返回地址dG5x*Bj`;Xb
|...#u#T9sOmAj
|栈底    高地址
-k`O1o$^:y)Qo 5yhAl5?*lA:E
2)使用可变参数所用到头文件和相关宏说明
x*iB5}#Ik!d {R[:yDW!c,o
在此,以TC2.0编译器为参考对象来说明.
8z E*I,V X%{P(}s QM 可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.MJ/} S}5O
此文件为:Gp!r"k$OJ#EK(|
/* stdarg.h+r8Z0D&at*y0cI
}[n)w5c:tgl
Definitions for ACCESSing parameters in functions that accept\5wGxJJJ
a variable number of arguments.
aB*u_TK w i 2P1cOXO&`
Copyright (c) Borland International 1987,1988
Al3@?d`8W All Rights Reserved.
K_`9A+S"d}"Uxl */9H.G l-aL
#if __STDC__
rn xh|4O #define _Cdecl
ns9f,W"I(H,] X8| #elseRj?#`:X,D6B
#define _Cdecl cdecl]1b8U;Vq3I xfL
#endif3?9I Ql)Dd;SY
0Nc,id6^doB%^.rI
#if !defined(__STDARG)
[.dU&@(O'Y #define __STDARGrA$j8S4{l2{/~

&S)aO rx)S#Y typedef void *va_list;
zZ:}fz t
n*l zp$k y%t` #define va_start(ap, parmN) (ap = ...)
w9i7i(n+MluK #define va_arg(ap, type) (*((type *)(ap))++)
$JF"f5C? #define va_end(ap)
;\!l(J D(Ls0}u #define _va_ptr   (...)T1K,oc`%c6Em
#endifhnXnj WAv
4o z$ESv-o3J
以上为"STDARG.H"的内容.V/?)w%z6F
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
AZ-{UPU ~"L3e va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list, K:m!vr/o n
parmN为可变参数的前面一个固定参数.!^2m._.j9lGR
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型. `#L8h[Xd e f
va_end(ap) 结束可变参数获取.|d4e{PJ

]e"H7vXT%r 3)可变参数的使用实例%r#JrOF7O

4CSG;Dz+F k 实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.T$Y,a-o*K2d]z
Ld:ym;X {NI
#include<stdio.h>
+|F8O(CD #include<conio.h>
D(u^*EH/y@ #include<stdarg.h>:O:F6c%gw
void tVarArg(int num,...);/*num为可变参数的个数*/'e,b,H H^l0S;H[$?+[
int main(void)
l3W ym+k'y5I {r@~6F'm p Fa*`
clrscr();
8po#iF2d#Z tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
+cY&[:R)T!u/mV-t] tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");Nw2GK#c.y
getch();%}.m'O3C9u3JV
return 0;
me [\7P0u }
3JO[r'w4Q h&j e_7^ void tVarArg(int num,...)
x`(te.^ Q {(E.cO%?pa
va_list argp;  /*定义一个指向可变参数的变量*/
$Q6H,^Av4Y X va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
3Ow%Sr J/M while(--num>=0)
zpr _XK U   printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
!O)Nr6Gv@.B#c;z     并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
*@KS @` oT6M7T va_end(argp);  /*结束可变参数获取*/
_7k;d5x${n return ;F;U9A\3La+_
}h]H {4oQh{7| D
ETRGG/K.k q
4)可变参数的使用需要注意的问题!g+e_Sx;r+T

s;FpJWXP 1.每个函数的可变参数至多有一个.
5C"l)Ss_:] 2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.N&oE~ oJ6M-][;h
3.可变参数的个数不确定,完全由程序约定.
-o/mP!F8u]'s G-dA 4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.ho1~ds#m1~3Aa
而printf()中不是实现了识别参数吗?那是因为函数 8N!URB tT
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
9m A&tNy [X} 的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 0p3t&k oK3o
过在自己的程序里作判断来实现的.
*ZsNmk 5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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


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