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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
! _0 C- O, k0 ]3 N; V它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
: K& D* I! T4 [% t/ d7 M) O% h可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
0 r3 p' A5 C- `定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有3 h1 p q* q7 H' c; A
实际的名称与之相对应.
& }, t |- T, T( F# x" t7 {由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.6 L7 S( Y) g- k
然而,更多地自由,同样也加大操作上的难度.
3 r. }, h; I' o0 i5 i以下就对可变参数的几个方面作一定的介绍.. P0 e0 c6 w4 L, l5 }% {1 @
4 L5 v! p* _9 _, o1)可变参数的存储形式./ U# {: V% q& v% s* a* J& f
( f/ Y, F% K$ [3 ~ [0 s; L/ d大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,8 b: Y" s1 W6 d9 T& \0 L- i$ L& y
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.3 h3 L4 E5 z& f% d W" I* L$ T
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,5 h% d" C9 A: B; s
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.; h F2 d. ?. `& C& ^. Q
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
# F5 q. K; w* M栈区:
V0 \7 z- l8 O# N& A
6 U g6 l" T: ?- y: P/ j! [ t( F( B|栈顶 低地址
- x4 O) _0 P/ ^# S% n' c
$ |; V3 m. G; H9 h0 _0 P8 h7 R|第一个固定参数var1
; {* p9 p! ^( e( l1 O/ Y/ k|可变参数前的第一个固定参数var20 K1 X& q, O% \( C3 ?/ D/ O! E0 a, E
|可变参数的第一个参数' A/ i# l' k" Y' b. X; S" z* I
|...: x# ^8 t& r! ~7 Z
|可变参数的最后一个参数
3 r' Q8 q: f8 Y" U9 ||函数的倒数第二个固定参数var3
3 B/ |1 Q: d1 g5 ~4 H7 p# e/ K|函数的最后一个固定参数var4) c, s" A3 h. v0 J& @$ |% F4 ?
|...
* x2 `; r( k) |# l9 z) w|函数的返回地址5 r" Y* J6 g) N, U0 b# W4 R* w; j
|...* X) n: @! p6 E9 X6 v6 I1 q. S6 ~. N
|栈底 高地址
/ c3 M2 t& I& V+ |
) ?7 ?9 o' T- T/ r2)使用可变参数所用到头文件和相关宏说明
5 U, X2 X* g( u( U* l+ T
1 @) G% T! p. J9 |在此,以TC2.0编译器为参考对象来说明.- L4 Q$ X2 Q* b
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
) W) ?. `8 l+ c: }8 m$ b此文件为:0 e6 _/ B; @" n+ {
/* stdarg.h. q" B7 x, C8 e
/ v6 i. I& `/ e2 o" E& B0 GDefinitions for ACCESSing parameters in functions that accept+ B& J) C4 c- J
a variable number of arguments.6 P( ~9 z) V; J* J
' i+ v0 R* f& J& WCopyright (c) Borland International 1987,1988
" f" E; b3 R" t) A+ p0 IAll Rights Reserved.
m; B' {+ f& J% q- `. g*/
" T& W. ]5 ~: Z: r#if __STDC__
7 d, s/ L0 K4 S; f1 z1 f2 R; P9 {#define _Cdecl5 J. M6 d0 n& N$ y p
#else
) { ]. Y6 ]6 \$ s* n4 f k#define _Cdecl cdecl
# r& L8 B% I+ \2 ?# T5 _#endif
6 M7 w5 v5 z( p0 D1 f7 l) h
- s, W. @# P7 m" k: F( q. X#if !defined(__STDARG)
1 O$ O& V* R8 t#define __STDARG
8 W* a( W# q& E- f0 y
! a- O& u- j5 @& w r2 ytypedef void *va_list;/ f5 O1 \2 ~4 c& o+ M) d; w2 Z: }2 T
! a7 a2 `" S; {( F: }
#define va_start(ap, parmN) (ap = ...)9 o1 D+ Y$ y: q/ Z+ h
#define va_arg(ap, type) (*((type *)(ap))++)
! I- j4 x2 `* ^* R#define va_end(ap)
9 ~* }# j. y! M+ R#define _va_ptr (...)% X9 h+ q K8 {& g" q
#endif& r: h( w! V/ o, ?; Q
d4 b2 A! {7 i$ [
以上为"STDARG.H"的内容.
4 c0 h0 F+ I2 P# L# }/ \该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
% {8 @- j/ v. B* Y+ Iva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,8 Y0 _' m" ~8 |
parmN为可变参数的前面一个固定参数.
! q! D3 L$ }7 Q2 }: Z# C Uva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
# X$ M+ i' F# U: p( ^va_end(ap) 结束可变参数获取.. k$ z( k) q- s9 O5 e' U
- t' q* E' R% ?7 P3)可变参数的使用实例) A5 w: q0 ^# w0 `$ \- z; x& F
# a8 g* V3 v" e& u) E* r i, U5 i9 J
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
0 a! q' y) S0 n* J h
0 c) s# S; _$ w" K) ]#include<stdio.h>
* `, O1 y( V/ u2 w5 }* x#include<conio.h>6 }! h' I# A4 ^# ~$ j4 ]/ F: ]
#include<stdarg.h>* M* o. A7 m8 H7 X& ^
void tVarArg(int num,...);/*num为可变参数的个数*/ |! `- X/ O6 O) j7 I. K+ _* A$ Q# F
int main(void)
. }. a+ \9 w X6 `. F1 h' E; e{
! S: D* {: b2 g9 Eclrscr();! v2 Y0 Z, Z! s3 F1 r
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");8 Z, e5 V) C- X6 L# Z
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");$ c; \) ?2 V/ s( j9 o6 ?/ j1 m* w' O
getch();
1 L' A/ } O" x S# N9 j. e7 Wreturn 0;
1 a- p" u q0 u* X2 h}* f8 }/ C3 f8 ^3 u* p4 _
void tVarArg(int num,...)2 c( a. [. q; M) y6 A8 u/ y
{' y( Q9 y+ V3 S- v
va_list argp; /*定义一个指向可变参数的变量*/
/ K% C* l7 X6 m" X6 M( @: i y( w) nva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
- d$ k1 M( c; uwhile(--num>=0)
. r+ i5 i) z: R% Y; ? printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
" z9 t0 x# S+ T [ 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/. K8 J+ m+ T. j0 j$ o5 z2 }0 Q
va_end(argp); /*结束可变参数获取*/
, a1 {' K( M l) X/ W8 D: Treturn ;: s" }; }. {1 ?! F% K
}% L+ ]+ ]. ` W6 j8 W
4 e3 {5 j: D8 Y
4)可变参数的使用需要注意的问题" g. k6 F' }: P' v% V3 m( S5 I1 W
3 M3 [8 g; f+ v, `7 r* k1 U1.每个函数的可变参数至多有一个.- T% k% k% u: T0 |4 G, k
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.) ~$ h! l( @2 y. y
3.可变参数的个数不确定,完全由程序约定.
* u9 j% p* d q' x0 J4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
7 g2 \1 C% W! l" C% N% w3 }而printf()中不是实现了识别参数吗?那是因为函数
4 S/ ?: V% M" ^4 k9 }printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
% S3 H( p: T' q: h$ g) v, l% h的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 4 h1 }/ U6 y2 M+ B) Q2 `
过在自己的程序里作判断来实现的.
3 y9 V' e" z; v" J) A5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|