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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
' z& O: I0 T3 _( z; L2 z9 r它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
[: y9 J2 G ~可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
* F' e( N# d* |. E2 x$ ^. v定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
, ]+ T8 ^) i4 T; f+ w实际的名称与之相对应.
9 x% s5 P b8 `由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.6 E |2 }: V0 s" L1 w( M5 l- Z3 W/ u
然而,更多地自由,同样也加大操作上的难度.
. H7 W3 R, N0 E2 l1 v; H以下就对可变参数的几个方面作一定的介绍.
* e5 ]6 k- V* u' t4 y! V' P& e! v( ~# q* b3 H$ n
1)可变参数的存储形式.
' u& R: m* R0 L- |/ } h
/ G. w9 W. n! X U/ v/ @% K大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,0 Q Z L1 l ]8 t0 g% u( q! a
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
$ y8 Q% l0 O, i# s在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,2 \# |) J9 h8 Q
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
/ k1 r7 Q& I7 o9 s( n因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
; K9 h+ X: [, k9 ` q N R栈区:
% X+ b6 s3 S. {. u6 {
1 J7 z4 l& F! Q; [* A|栈顶 低地址. c. `8 T: W l4 \' G5 n) N$ z
& I, z, a; M6 w5 F0 A& [. P
|第一个固定参数var1
9 I8 @$ K: X$ g% u4 w3 E|可变参数前的第一个固定参数var2
4 k4 ]1 j) |8 I' z% g; u6 P|可变参数的第一个参数1 z3 @+ R: m4 M1 ?4 w+ U
|...
0 w4 f6 d" D3 l* t# K|可变参数的最后一个参数 H$ V4 u* r9 e
|函数的倒数第二个固定参数var3' {+ Z. {! `6 S* _+ |
|函数的最后一个固定参数var4
+ }7 H# p: W% V: G* j/ g6 B! V|...
0 I6 L) z' e" p|函数的返回地址; D% F9 e9 Z" Z ?4 x2 T
|...& `# |! p: m& E) J" x! ], G3 X
|栈底 高地址
& c4 a" z. h" B% F" U+ n% M# Z& |- L) r
2)使用可变参数所用到头文件和相关宏说明 |1 W- ]- C F' Q4 S
3 Z6 G+ J# Z" p% P# b- m) s0 Q在此,以TC2.0编译器为参考对象来说明.5 `2 q5 L; w; B. E* T' D3 O* n
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
) m- O! r f- \& C& ^2 ~6 r此文件为:8 m# D* X* ?! {; s0 o- Y
/* stdarg.h
$ L. Y6 N+ c7 J: r+ Z4 z
5 y5 N1 o; g: l: [# { m; k. V: ?Definitions for ACCESSing parameters in functions that accept" D8 D. \5 ]. Z6 J5 S
a variable number of arguments.
. W7 a' I: S9 a3 q) u9 V' E; G0 A# {/ W: @2 x( A
Copyright (c) Borland International 1987,1988
9 K# C! [. S- r. V* Y5 g& e: iAll Rights Reserved.8 f% i) f! s/ g, m5 i+ z) e- X: }
*/' r4 d, q1 \4 n v& \! o7 N
#if __STDC__
* I$ `& V0 b7 v#define _Cdecl
' N8 f7 Q+ a B& _( B5 r#else
! o$ n5 k! E3 G: G#define _Cdecl cdecl
3 B5 |6 N4 c% H1 t3 @, i& M2 z3 x#endif4 I7 M+ X3 _9 c7 }& o z
4 ~ d4 z& r# U7 r! Z* q* G
#if !defined(__STDARG)8 Z: U" d& a9 j; B
#define __STDARG, s) u: [! V6 W3 }, y
9 r+ e2 @# }8 u: ^5 z: o
typedef void *va_list;
3 T9 f3 L, q- T0 _# K2 c
/ U- t/ I, f: `2 A R5 }0 `#define va_start(ap, parmN) (ap = ...)4 u7 k3 ~& i8 z$ s. s
#define va_arg(ap, type) (*((type *)(ap))++)( ]- j: C' d8 s, W: |
#define va_end(ap)( A2 M; s! J8 v- @0 ?
#define _va_ptr (...)1 r0 ~% [- O0 D8 }% J
#endif
t$ h% @' o1 ~3 v5 D U2 q5 d$ y( Z2 ]; H5 X3 Y1 _0 u* K
以上为"STDARG.H"的内容.
# \! F0 }$ b$ N7 Z- Z. C$ P该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;9 i9 W4 p/ @% }) i0 f2 O) b
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
; Y8 e0 x" |7 r- Y+ B- k- y& u, DparmN为可变参数的前面一个固定参数.
4 `6 X! x- O- Z' ~% F1 Eva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
) x& C; s Z0 J" t- wva_end(ap) 结束可变参数获取.% ]- D" i- g% z ^9 _
8 l9 W& _: H( r( k
3)可变参数的使用实例, d; j! h0 }* g% g( U
p# c- H# V5 |; _8 o. y实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
/ W1 X' R4 X, a, n( Q: N H. l. t$ {- p, B2 s$ u
#include<stdio.h>3 i* ?; u# p( z5 |" C
#include<conio.h>
2 \ a+ J, }! E) E; J. O#include<stdarg.h>
8 L9 o1 W6 f( n& ^2 h% fvoid tVarArg(int num,...);/*num为可变参数的个数*/
1 M' s) h& X8 a# Vint main(void)
+ @# A* z* A7 R/ P( v: s{2 D( l: b( `8 m
clrscr();
% H! M! Q3 {+ W; U( LtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");- m4 E/ ^# v& O+ A2 `
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
& W G6 `/ H; I8 F2 B: Ygetch();7 } t* s* }) s D, V" m
return 0;9 {# h1 Y! }% _ [# J4 d2 l/ v6 }
}0 b/ T/ J4 r; i: J# _
void tVarArg(int num,...)
@0 v- V: a) d8 a# d{
$ ~+ r+ G [ b9 L. wva_list argp; /*定义一个指向可变参数的变量*/3 [2 k H4 H7 }
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/. z- G9 A7 v' T, j( I
while(--num>=0)! @8 i/ T# N6 k/ Z
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
* ^. y/ h- z% y/ n 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
, N! g8 a' J& [! F4 Bva_end(argp); /*结束可变参数获取*/
7 F$ R3 G" d, f" h0 creturn ;
* M& p# Q: J. @5 j, h}7 L7 `% }: o! `# G! b1 v
; L+ M* m! N9 o% N- g3 y
4)可变参数的使用需要注意的问题! L1 a7 N, V* a3 a
0 h# ], V; ], q. X6 O1.每个函数的可变参数至多有一个." j' Z: ]/ G! `; {3 n/ }
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
5 C' R' x/ ?! q* [. m2 G3.可变参数的个数不确定,完全由程序约定.3 i$ t: X2 n/ |( M0 k
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.5 A4 ]' z% f- g" F) k2 I
而printf()中不是实现了识别参数吗?那是因为函数 ' c6 n8 P9 P- s: }4 i
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
1 T$ v, H$ _( W+ Q9 D3 k# M4 `0 b的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
* m+ g( T/ l+ C0 X过在自己的程序里作判断来实现的.
# M3 w6 z1 i) O3 k) L$ }4 a5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|