  
- UID
- 133
- 帖子
- 51
- 精华
- 1
- 积分
- 186
- 金币
- 55
- 威望
- 2
- 贡献
- 0

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
/ ^/ q8 H/ v' a; D1 G: N0 Z# L它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.; `3 L& b$ Y- w4 c2 q9 [
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不- s6 b" D4 W! H7 P; e
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
0 j. H+ J/ a" E O实际的名称与之相对应.% r9 j! y' R; F B
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
# l5 e8 P) y/ Q: ?* {然而,更多地自由,同样也加大操作上的难度.
. N" |0 u' O1 i- F$ b0 _3 G以下就对可变参数的几个方面作一定的介绍.
0 M z; W% _- y" U% Y0 s1 t
8 C# \0 x% d0 f( D1)可变参数的存储形式.+ }# x& C2 u- P' j
' s- I8 y" m5 x" y0 ^大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
8 N4 M) ~, m) q% L* r# p: r存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
1 l9 ?* ^& A; `. O在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
$ n/ p" N/ } @& E这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.! `; K" g8 `. Y( e2 M$ z
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):7 c8 t1 |* l( Q6 w
栈区:
; n, ]1 T) O) z, B9 S5 o
5 l: t% L+ R9 Y7 s# ^6 A, B|栈顶 低地址5 M4 K0 a* V7 b/ T2 s" Z7 }
* W8 ?; ^8 d6 o# V" b% O|第一个固定参数var1
! K. n( `# h3 L7 t5 ~5 J) g, y3 [4 W|可变参数前的第一个固定参数var2
+ J. H& C. e1 b5 n- [1 K|可变参数的第一个参数
, y' t6 [/ M& a) P8 `|...
% w4 P v! x" L5 ^/ A- ] O2 _9 q h|可变参数的最后一个参数
3 [9 p9 h! A k: N1 z7 c|函数的倒数第二个固定参数var3
& d5 j! i" A: m$ k3 Z$ ]5 _- ?2 `|函数的最后一个固定参数var4* X- D H& P8 V. W: d
|...
% z1 q8 Z9 M: i/ D5 X+ R9 F- C|函数的返回地址
0 w9 z2 i) ]8 ?7 ]9 Y0 k|...0 m, ]8 B, {6 D/ P: ]6 R; E
|栈底 高地址
4 T: G' a+ X7 V5 Y3 ~# H$ `& k& h* m3 o
2)使用可变参数所用到头文件和相关宏说明: Z& e& z1 }+ ?; O- r
( [% o8 G+ x3 h6 a0 Y
在此,以TC2.0编译器为参考对象来说明.
/ o# V o, \- U' [可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.3 m" K N: c8 z6 T8 e' U
此文件为:9 t4 d% b F( F P/ O: P
/* stdarg.h* F9 A d+ H7 o2 }" [; \( |6 I+ A* n
" R2 Y: ^* F& y# J2 a& L! l& p
Definitions for ACCESSing parameters in functions that accept
3 ~$ s: l3 q4 Z: C/ @ T% x+ la variable number of arguments.( f8 J1 y0 P3 V' ~8 v( I2 W1 m ~
6 Q ^& L/ n5 p6 lCopyright (c) Borland International 1987,1988/ P; w) |2 H9 a* W" n
All Rights Reserved.
4 n8 D& V) x& v; r! K*/3 _( o C; l& T
#if __STDC__
) j& @1 v! d" L6 D& E/ ^/ H2 ]8 Z$ w+ w#define _Cdecl% q- `5 {2 }3 J4 t8 ]
#else* a' u5 \0 y" F; ]! T# B7 x, m
#define _Cdecl cdecl
' V. ~3 R, r9 m& e" R0 s0 K* r7 y#endif
6 R; j! i- x \; ]. |. p ?
3 S x/ U1 V! G( i" h1 p: P8 e#if !defined(__STDARG)
0 e" w* Q1 @+ ^, z#define __STDARG
+ Z1 Y* A* R9 n* A6 A( L; R" n' K% H; I* m& ^% D1 r3 O
typedef void *va_list;
4 ?5 d x1 [- U- }4 `7 i o, [6 c1 ]& Q) P
#define va_start(ap, parmN) (ap = ...)
( d/ [5 P6 c1 x% ]- |) p: X#define va_arg(ap, type) (*((type *)(ap))++)
. F4 u6 ?* R$ A; l#define va_end(ap)
4 U# L; q+ B B9 |% N; c: R* e#define _va_ptr (...)
% ?: o9 d1 z" J7 ]5 q/ b#endif7 `2 Z1 u- s/ e7 i' Y1 @4 D
# h$ K' ^- m8 F% X. m- P
以上为"STDARG.H"的内容.
0 Q/ i. }( l0 a+ B该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;) g- {1 E1 V' f
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,9 |5 Z/ _9 C* w4 }6 g1 K
parmN为可变参数的前面一个固定参数.0 l4 {( H: [' l; r! Y; h7 Q+ Q
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.8 L. d# o* E7 X1 D
va_end(ap) 结束可变参数获取.2 O' o4 h: [) y/ O! [- B( {
9 }, Y0 S/ N! w( S& p* ]: ~. M/ r& J3)可变参数的使用实例' b/ c6 h# B) Q2 U# a- X
" d8 T8 o* [# W& L& ]1 F' `9 {实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
4 A, J7 A9 v/ y# z. u: n3 v: o. m7 S) O9 o+ _* i
#include<stdio.h>* h4 p1 |( O0 u( x% V" T9 u
#include<conio.h>
5 ?( ?' z3 y/ t5 f! e$ S#include<stdarg.h>. u6 `+ {! L* S3 ^" z
void tVarArg(int num,...);/*num为可变参数的个数*/: t) D8 f4 a( s% D
int main(void)% s, S- I6 A0 y0 |7 s( Z% l
{
2 a1 {& ~; J% d$ b$ f) P! T3 Y' Iclrscr();! p5 O E- { U! O0 l/ b: _
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");! L* u( l+ R! m8 }1 }" L
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
- L3 U: E6 W" Agetch();
1 n- s$ [( o( y" }$ ^* E7 x$ O) qreturn 0;/ ~2 h- v6 j. ~5 w/ [/ k) g
}
- k4 k& z8 \7 k5 F9 q+ Y- O- dvoid tVarArg(int num,...)
7 O" O# r9 L& Q2 o{
& R/ w/ O4 _ M( Z; ]va_list argp; /*定义一个指向可变参数的变量*/1 W# W( W! C7 R W
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/3 T, S- g" K6 i3 T$ D
while(--num>=0)# Z# h, v! N) X" D/ C" f2 U
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,; ]8 W+ U+ F1 r& h% p) B$ O
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/7 _9 a2 B- D* h& ^6 S
va_end(argp); /*结束可变参数获取*/
- b# u* l8 P4 T) Oreturn ;5 r3 n0 }) O$ n! l
}
9 _0 F" F2 o1 L3 I( t0 V+ R6 A4 N: h* O+ U+ i% h Z' @
4)可变参数的使用需要注意的问题
0 |' `: c1 u$ A+ N4 j$ w$ p1 l; c2 j3 L5 d: v+ p+ }4 n8 O7 c
1.每个函数的可变参数至多有一个.0 _9 y, L4 J: s" U8 b0 B' _/ t
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
/ S! Q& n( R! C: \3.可变参数的个数不确定,完全由程序约定.) `0 m K6 f7 \' z2 ?
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
$ g1 ~) G$ Q' c: i而printf()中不是实现了识别参数吗?那是因为函数 ' U3 @7 [- Y% {1 G" Y' D
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
; x# V$ e, n. ~1 m! _* i7 L的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
( c( P0 n- [- x( h% r. C过在自己的程序里作判断来实现的. 4 B, r' x$ f( T( \! W
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|