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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.9 p, z2 t. \- D/ l
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
4 s2 F- C' T: Z1 B1 j可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不! t4 k; e: f/ S5 m, R' q( ?0 }0 j
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有 P' K8 |$ l2 L9 I3 T7 O8 |
实际的名称与之相对应.+ N2 B) S0 n! @+ k; S! w5 U# b1 e
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
, {* w: s+ _8 P7 b9 x% X6 H' x$ }然而,更多地自由,同样也加大操作上的难度.
* A/ _5 F, \6 \以下就对可变参数的几个方面作一定的介绍.
* T1 M* g/ ~; E& e3 o; a* v n: H! C5 _1 A K$ @* D! \4 x" J# i
1)可变参数的存储形式.' Z% D$ {* o; ^, M: ~
9 E4 I ^* O% N9 N大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,! @' [2 `. F+ t8 l) t
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
$ }1 l! K8 d5 r5 j9 F5 J+ Z& @在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
: ]% \% B4 W( G7 H" F! Z2 V+ M这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
/ f7 V. e( I. j% u: \2 o" m因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):% ~3 `( D; A% i% T, O0 z: k
栈区:- {% j% r% ~% |& @
2 P9 a9 C a# a) D6 ^4 g a' I0 c|栈顶 低地址1 _, s; |) |3 c2 b* o
9 s3 a i" h0 Z" K! I
|第一个固定参数var1
" K( J" \: l8 E2 n; a; o|可变参数前的第一个固定参数var2+ [' k. {- e+ M7 s6 _* @$ T, S
|可变参数的第一个参数$ M$ j4 V, \! M! ~1 T
|...5 U. D& K1 M$ f' @1 o0 w0 i
|可变参数的最后一个参数
8 _0 U W, x6 F1 _7 C+ g|函数的倒数第二个固定参数var3
* l' l U& H& M1 ^/ j( \0 z|函数的最后一个固定参数var4
0 k/ A9 n. b8 ]& j/ G9 S|...
8 x5 M4 G& l7 T A l. w|函数的返回地址
4 N/ O- g4 ]: d! G|...
; }8 f8 p' ]9 q W. ~|栈底 高地址7 |* J+ Z* b+ m1 u
, t5 e9 w9 I3 U& D) @" W% K1 ^- {2)使用可变参数所用到头文件和相关宏说明
" z# {4 u0 M/ B2 i
) Z9 i2 p p. [! L在此,以TC2.0编译器为参考对象来说明.
: v. p# l% X. w, s& [! V# ^可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.$ ~2 |7 C$ V/ t$ b
此文件为:6 N% K, r: G/ ~
/* stdarg.h
$ B- \% F9 v6 F* K9 w
1 `$ ?5 e" ^. K; a5 _1 {: y$ K5 lDefinitions for ACCESSing parameters in functions that accept
, y( x0 y! {4 l5 q4 N) }a variable number of arguments.
, N9 i; `3 c2 o' U
; p2 G) J q! w- o1 `* eCopyright (c) Borland International 1987,19888 ?8 o, L; |! i1 z
All Rights Reserved.
' l ~4 g4 [2 n" ~' P*/
2 I. r8 ?0 q0 Q# _2 o- V$ k#if __STDC__
8 H i5 U6 E: g9 g; C#define _Cdecl
1 W. e) ~( ], K& G0 Q#else
, r* Y+ i! Y! ]; ~0 B" k5 ?#define _Cdecl cdecl$ w2 y6 s% s1 G9 u4 d. Q q
#endif
& w+ j2 @" ?& C% Q& o' I& f
6 f6 d2 e3 S) J+ m: [#if !defined(__STDARG)4 V$ \- J5 D) R: C& _
#define __STDARG9 \ M) V" G4 i. k% I2 W2 b
X$ o. I/ x1 G% T$ A9 `typedef void *va_list;- O+ W, W+ A+ E
2 `2 \, R; O. Z1 I( K# _4 I' R#define va_start(ap, parmN) (ap = ...)3 Q; m4 @* j; T, I7 q0 |7 v
#define va_arg(ap, type) (*((type *)(ap))++)
1 |2 d4 ]- K& L J- p1 U; y5 q#define va_end(ap)8 X* Z* o& L3 f
#define _va_ptr (...)5 b' J9 ]; v( Z+ d! B7 ~/ u3 L, X
#endif
8 A" C5 H2 p) |* s5 O5 C6 z; c2 B f; O, ]" ~( X
以上为"STDARG.H"的内容.+ R% L. ]+ p" W* d0 G. W* H
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;' ?7 x1 V5 Y* }/ ]( Y" L# w4 r
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
. X4 n c4 w+ _8 Q9 ?) L: EparmN为可变参数的前面一个固定参数.
, {. \4 j; ^; b, t* eva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
; i4 a' e; G# ?/ f- rva_end(ap) 结束可变参数获取.
7 ^0 W. ?& s" B6 C g5 V
; I1 F- h6 {0 X1 S* J- t3)可变参数的使用实例9 ~3 T( m+ g- V4 o5 `
, a# U' q' v+ c, ~8 ?, S7 ^+ J
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.' F) O! }4 ]$ S( z! z; K' ~4 _/ t
& d& `6 O" R2 H, t#include<stdio.h>
& D s! }2 Z" T5 d2 H#include<conio.h>9 z3 g# q( x) t# }
#include<stdarg.h>
+ n/ S% b) R: V! A' v- p3 X* w: vvoid tVarArg(int num,...);/*num为可变参数的个数*/
) C1 C" a, M N3 c$ g; |7 Hint main(void)
8 H( ?( O, w& a% v; r9 z. h{5 ]4 h5 g+ D+ O: I9 B
clrscr();; r/ k) ~: Q0 A. F/ h# ]$ A% T
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
$ [; R* g+ [/ V) ^tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");, B& L3 ^( [0 @: u& t5 R
getch();
- x. ]2 J1 \9 _; T! l+ qreturn 0;
2 [3 e& I# a4 F}
1 K; S* t4 C5 ` Y! Zvoid tVarArg(int num,...)
9 L0 ]. a* m) z( L5 k) s{
' j# V& c% |& ~; j. Fva_list argp; /*定义一个指向可变参数的变量*/; u: p: q* G0 s/ |
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
! |# X5 P: h" C* r' Dwhile(--num>=0)! Z1 B% W, Q8 q5 j" J* x) j
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
. O v9 ]6 R2 ^9 }! h, t) \6 C 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/* h2 q) l4 T( p* N' A: ]
va_end(argp); /*结束可变参数获取*/- ?% O5 ~9 Y6 ^& d" w0 i
return ;
. a& z* q7 A6 p}
/ q6 o% ]' x+ N; b. Y4 k/ n: n/ @- u
4)可变参数的使用需要注意的问题/ i% J) j2 _& {: Z) O7 U
8 m$ z9 f* T- t1 S1.每个函数的可变参数至多有一个.
. y( T: f u0 c6 d- Y1 K2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.! }8 B/ y Y1 z
3.可变参数的个数不确定,完全由程序约定.
! b" w1 ? L6 O3 F( T- S4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.3 {! ?7 a9 b6 f4 b
而printf()中不是实现了识别参数吗?那是因为函数 1 u8 Q) z5 j! ~6 e. b) t! j# l
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
% G5 Z0 k9 j, O' O x" e2 U) N的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 " f" v. A# n, t( W! R
过在自己的程序里作判断来实现的.
$ _( y+ ~1 X/ @5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|