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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
# [+ A" M) l, X( K a它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.1 O* b( Z; l, _1 U& ^$ t! W
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
6 T0 j5 H3 m2 L' S( X定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
7 E$ W% }* ]& Q+ i实际的名称与之相对应.9 s' O0 O8 X' d7 a; L, S: x
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.8 K) |1 Y2 u% ?5 o; v
然而,更多地自由,同样也加大操作上的难度.# x: u/ x" z1 u6 }* t
以下就对可变参数的几个方面作一定的介绍.
Z% V$ S+ u4 r6 P
- E' }' g7 w2 b1)可变参数的存储形式.
! w* U8 X9 }: i/ Y6 a3 [" O, C1 X# x/ V, o2 A# W' m
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,, M# P4 a6 @7 }' \
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.. y9 j: i) J$ M0 H7 V) X6 y
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,) |# s5 G/ i( Q9 ]
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
1 q4 Y- r, h6 b F! Q7 x$ C因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):8 |+ p$ F" u# o ^
栈区:* K: n' B9 }0 L; W; X
% j0 t8 \' E) f0 p
|栈顶 低地址4 F s9 I6 r8 L q+ F
( [1 o0 H; Q2 X4 D% h
|第一个固定参数var1
* s7 X3 d1 v4 o+ k! ]0 ^ h|可变参数前的第一个固定参数var2% p- |0 i8 Z# X7 t) q
|可变参数的第一个参数
1 p+ }' j7 r8 w% M|...
0 r2 o$ Z X D: w; D% B|可变参数的最后一个参数
$ d, ?( \3 Y' B1 X) K; Z d; J|函数的倒数第二个固定参数var3
D; P/ ^9 V. i8 U0 X6 M7 U( i|函数的最后一个固定参数var45 z. I1 e& n, m
|...
' }" S+ B& @' J+ n3 [' `|函数的返回地址
% ~ d, K6 v+ j( Y( v9 t$ Z|...
, n3 T+ ]5 R0 U5 G; ||栈底 高地址4 ~" W* ^( J% j( b1 O; D& T! E
Z/ \3 a2 Q4 e \2)使用可变参数所用到头文件和相关宏说明. g2 |2 O' |3 Q! V+ A6 B$ A
+ q0 u, M* i% k) S9 l, O
在此,以TC2.0编译器为参考对象来说明.
5 I: c6 ?% y: A! p! L% J2 N. {可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.5 l' t4 T1 ~4 A( a: X. M G1 f
此文件为:
# w! Y& B/ S% v& ^/* stdarg.h6 \7 n+ f) |/ p
, u& Z- k8 G/ }; m: R- xDefinitions for ACCESSing parameters in functions that accept8 }1 o' u( J" M+ b. i. u2 M# m0 e
a variable number of arguments.
- f. b. O3 w4 @8 C5 a
8 h: v' t/ @) s/ Q3 M) N- XCopyright (c) Borland International 1987,1988
% ]3 z3 W% j$ W! v7 o/ PAll Rights Reserved.
! o) h$ j8 I+ b/ c*// E) u2 n4 P1 I+ L# B0 G, t3 n
#if __STDC__! u' {8 W2 s* ]. J
#define _Cdecl( h R0 F3 O v8 I, `
#else% b6 \, j& j4 ?. O; d5 Z* R6 }
#define _Cdecl cdecl+ u' d2 {. G$ @9 o$ T& ?4 ^
#endif/ B1 _5 U. i* y7 I$ @. R" a
2 H- K- z; l: z/ o- o7 c
#if !defined(__STDARG)
2 g) z" p" i$ T1 Z6 w#define __STDARG
7 t7 z! |0 N& i: t4 z3 Z. a# W7 `7 g! P1 b% n
typedef void *va_list;
/ N1 x0 f/ J! Y% [: w7 A8 y) M) v* Z7 w) ]
#define va_start(ap, parmN) (ap = ...)
% Z8 V7 y; v. ?/ b3 {( c#define va_arg(ap, type) (*((type *)(ap))++): V9 F: N/ k! S3 [" o$ C8 G; y; w
#define va_end(ap)
- l1 X; `1 r' G, j2 e( O0 S+ g% f5 V#define _va_ptr (...)
! `* k" b, X6 ~1 b1 n+ L2 }#endif
; t! J. W6 O6 ?6 I+ q( O, W' w8 p# a2 B2 y: m/ A4 L
以上为"STDARG.H"的内容., M0 D- N7 d. _- D! K/ i9 O
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
/ x. O1 K% H( E5 p) P5 s+ Q, tva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,8 m. Q8 Y9 E. z" n& D7 P' w, Z
parmN为可变参数的前面一个固定参数.
1 Q( B) u/ N, L* x' Eva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
- l R$ {$ @3 \va_end(ap) 结束可变参数获取.
' @+ \3 x" X9 o: r8 `) Z9 }
' z' e8 o6 R* M% {3)可变参数的使用实例' q- c# @, A8 y% @2 C Y- s
. B: `5 @! ~$ |/ D9 \2 m实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.* u2 F! [8 c" |* V) c
# N7 G1 R- Q/ x$ @( O$ h#include<stdio.h>
# N3 j. o+ w9 v6 i) F& K#include<conio.h>: L( f& _/ ?* \
#include<stdarg.h>: r+ I1 {5 v$ h2 t% x
void tVarArg(int num,...);/*num为可变参数的个数*/: F! w( V: o7 _1 R( Q
int main(void)
5 F! W: [& W" V& S* F) e/ S{
! e% L2 W! |4 L! b( Nclrscr(); Q# N1 o5 j: y* S
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
! O9 w/ |! v' m8 F, t) ~tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
, ]9 K: V8 b: X% I/ u8 K7 u1 Ugetch();
1 C" j9 j! Y: K' Z0 kreturn 0;! h$ N4 D* T3 h+ R$ m8 H
}( M' V+ o4 L- o9 ~+ s6 h9 j, p- D
void tVarArg(int num,...)
3 I$ S* `# F V' N% o X6 G{. ^- d0 N& y" H7 D/ r/ s4 N9 P* h6 g' D
va_list argp; /*定义一个指向可变参数的变量*/
9 R9 t) \! C7 E4 ~# g* g" _, `va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/0 f* Y" F) u- L& `* ?$ [
while(--num>=0)
7 h2 E, i& f/ ` printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,1 a1 z, U) m* g* ^
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/' R: b+ d* i1 E+ H0 F
va_end(argp); /*结束可变参数获取*/* |8 h2 V6 c. E. [2 D
return ;
8 f/ I H" l" _' H" }: U}
' p. d- B9 y, u! l0 s' J$ k( F2 p
% H. Y! X* D8 ^) m7 x s& O7 A1 h4)可变参数的使用需要注意的问题
; M9 l0 |9 \7 X" {5 L. @* a
* V: t }8 |$ F. ?7 h. M1.每个函数的可变参数至多有一个.
% M& k; a1 i* ?+ @! r2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.; h$ w. u/ d A0 q
3.可变参数的个数不确定,完全由程序约定.
5 P: f! Y/ m* P; [4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.: C$ X* c! N3 }9 r( Q$ |, ^
而printf()中不是实现了识别参数吗?那是因为函数 " r! O' V/ W; _) @+ a* \# B9 ?2 ~
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
% ]7 Z4 T7 ?3 F的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 8 O, Z! g/ k8 D% u
过在自己的程序里作判断来实现的. : C: ~0 Q8 g2 Z D8 d2 D9 Z) x
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|