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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
5 x9 n% Y' p, Z它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.. c; d% d$ `3 u7 b- O
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不0 B4 [% g ~9 d
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
, d- J/ n# b3 L8 [2 q7 U4 \实际的名称与之相对应.3 m3 D. N; H1 h" h) }" P
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
0 i0 d* x5 }7 }然而,更多地自由,同样也加大操作上的难度.
" a- f1 w. U1 w# v) S' }6 @以下就对可变参数的几个方面作一定的介绍.
, R( M. ]6 `8 o6 R9 C( S1 R# Y7 [1 f8 ~, T- w+ {
1)可变参数的存储形式.1 }* G9 l. a2 ]1 y" |
/ Z) C8 x4 L; H* V大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
- P+ `, D8 e: }$ Q0 h存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
- |/ W8 z& x4 d+ G6 B X, z i+ L在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
$ H2 k: S$ p& {) [: g$ W这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
5 g0 H2 g6 A& v$ ~- |7 e因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):8 O; G4 [4 o8 s" n0 z- k
栈区:
8 C9 G% y, T) S) w4 W! R( J1 B3 X' B1 Q
|栈顶 低地址( T+ a! W, j9 [+ Y
. a3 `) {: w/ w$ @/ U C+ r- w
|第一个固定参数var1
2 R0 d2 T' V5 ^! V|可变参数前的第一个固定参数var26 J2 i u R# l2 L; P& V
|可变参数的第一个参数/ `& f4 ?6 h5 ?! B- g5 g5 [# L
|...; \- t2 K3 s: |
|可变参数的最后一个参数 f+ B1 d! L" E& A
|函数的倒数第二个固定参数var3
0 B7 x3 Q. W0 q H5 g, d, l|函数的最后一个固定参数var4
i; W- s+ ` b6 T2 e1 [8 B1 i: y|...
& Z) ^5 X: s/ u ?; `|函数的返回地址3 d+ b7 j4 D$ D2 _$ i. p) L
|...- P7 I- c' f1 T5 K$ B% z/ B
|栈底 高地址
9 O d1 K/ ^9 K. z% c; k
0 O7 \& B+ e8 w" x' X% Y' S2)使用可变参数所用到头文件和相关宏说明
5 m+ T, e8 `8 P; T0 s$ N: `! T, Q% ^. O
在此,以TC2.0编译器为参考对象来说明.+ p- @2 A" e' h+ ~
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
4 k. k- q5 z. u( A# c0 X此文件为:
3 r8 ]8 y" e n/* stdarg.h
/ z+ t/ t) W' o* ~; K, h1 g& m' M/ {& [: Q9 u$ m. |
Definitions for ACCESSing parameters in functions that accept
* {7 L0 J) d7 o& L; ta variable number of arguments.
6 q# v8 `# B5 t- L7 W
2 V8 B: ^ t0 iCopyright (c) Borland International 1987,1988
4 H: R! m% Y( A$ BAll Rights Reserved.$ G+ ?1 O/ C' E p
*/
5 W- H" O" Y8 [0 b6 D; j% L#if __STDC__5 `+ Z. J" h3 s: `
#define _Cdecl
; V; q3 g4 C6 @- ~5 l" e#else
1 n, A7 M, S# j: z: d! [+ D#define _Cdecl cdecl
/ @7 \1 O; u' P) {, Y5 _#endif
( B5 T! k. _1 ]1 T7 _
& N6 E) { `' j#if !defined(__STDARG)
" E# b" `4 ~$ \# G+ D5 T3 f% t#define __STDARG
; c) L, l5 E2 L7 f" \
/ @& j o p" v& O- R B8 L# |" R0 stypedef void *va_list;
+ ~9 _, P5 s$ H* Q4 |3 a* `+ s- P0 m' i1 x0 M; R" I
#define va_start(ap, parmN) (ap = ...)
. a2 W' p! `/ i7 g5 ]8 V: P#define va_arg(ap, type) (*((type *)(ap))++)& l' R2 x6 L: m# \$ V) z6 P
#define va_end(ap)1 p7 ~. t% ]$ p% F# h% Y
#define _va_ptr (...)
2 z- ~0 A+ l- [) ^#endif
. ~0 ]* g% J. u6 u2 E4 G: p3 c. V/ s! z
以上为"STDARG.H"的内容.( M" s* H, L: _! @8 d0 P' M/ I
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;- @5 }7 C6 j) P7 c* Y: }
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,; U! V2 G& L# k7 G# Y" _" Y8 N
parmN为可变参数的前面一个固定参数.7 p/ ]9 K, q% ~" R6 t+ a9 i
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.; _: z. v1 R6 T+ t
va_end(ap) 结束可变参数获取.
, `" r* T, ~( @+ m5 O! f
1 A- l# d& ?( K+ ?7 [8 j3)可变参数的使用实例
. s0 A; {- y! y; V2 l: S. U: @* ], B+ v/ {- G) \8 Z# c: m3 N3 ]
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.- @+ \ l$ Y& W7 C
" o: Q3 [- X- l' o0 T' p
#include<stdio.h>5 K% Z/ F7 K) W1 D
#include<conio.h>
6 `+ o$ {4 ? x: E9 {% K; h+ U T#include<stdarg.h>
( ^( n4 k, _0 G L/ P( [& S. |void tVarArg(int num,...);/*num为可变参数的个数*/
# E$ h9 H* I# b: p* l& o" Gint main(void)
, S5 ?4 v) M4 i; e/ |{, {/ ]+ m7 e' W
clrscr();
& a/ x" H8 a. vtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
$ {2 i) Q+ e) b; E3 b, RtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
$ A5 E1 _5 J" v- g0 t! Ngetch();: w1 I+ R$ f& o" A; i
return 0;. s6 q. R9 Z$ c2 d
}. s8 v j/ |" |8 a6 A9 ]
void tVarArg(int num,...)
8 H X$ Z" V5 A" O{7 I4 E, g1 w2 u- r1 ?8 b
va_list argp; /*定义一个指向可变参数的变量*/
- }7 q1 v8 f% G( Z4 ava_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/6 ^+ |+ Q8 z- c* I: o
while(--num>=0), a2 F6 o. k: ?7 d& G
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数, ?9 q6 v* j) n/ x+ G8 M7 ^$ v
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
& N3 g7 N/ n) g! O* s4 mva_end(argp); /*结束可变参数获取*/
& ^7 e5 h+ Y: V( R- Treturn ;
2 D7 x8 y% A& i$ ~8 W}
* @% [# { t) ?8 O& B: }5 k( ~9 B; T5 ^2 I: C, d
4)可变参数的使用需要注意的问题
- Q* O6 u E$ x, h$ M
4 s. t9 `2 g' p( b# \, {1.每个函数的可变参数至多有一个.) Y# N* D2 s2 F5 b) S: p+ w) A
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数." a* ^% B9 q! O: t; E
3.可变参数的个数不确定,完全由程序约定.
1 Q4 I( X, \" Z: P/ g S4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.. L7 e! |( @$ X9 W0 v' O5 T
而printf()中不是实现了识别参数吗?那是因为函数 $ z0 Q1 n, X" _; [
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
$ F3 g; l, A4 \8 W7 g8 j的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 . C( k7 h6 B" S8 ~3 P6 |) {1 U
过在自己的程序里作判断来实现的.
; j! O9 P" R9 c% U2 G$ z# Z4 I5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|