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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
6 V- z. r6 Q4 t; y它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
6 I4 ^7 A- Q7 M# z7 w9 F+ ]0 x7 W: E可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
' w/ M8 |9 b! g& k# ?定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有1 r% [- ]) M- e" b
实际的名称与之相对应.
( |2 x( Z3 d/ Y3 |+ _# E( u. v; \由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.* B6 `" z' J& m7 P
然而,更多地自由,同样也加大操作上的难度.
- c* h: S- ~/ L$ v以下就对可变参数的几个方面作一定的介绍., p; P5 d6 N* |% i+ r
$ m( u5 s; [% I5 Z& j
1)可变参数的存储形式.
: E s% g& B; [9 D# M: t5 R l( Y, b! c% X( r7 E. \
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
; d/ Z, d' t$ b存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
; Y4 y; H: t5 E9 j在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
& P1 M" w" A1 b- E8 y& w( Z这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
0 ^& X' y, D7 Q* x4 {因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
$ Q1 H: a4 X6 n5 Z+ @( i+ _# C栈区:' N% l3 W. {: _4 i5 n8 M
4 K$ ~; W8 s- J+ ^( m; c
|栈顶 低地址
0 \5 \, f, z3 A0 ~, E
- s( x9 \; q& t* K|第一个固定参数var1$ T' s" |. W) t7 J5 e2 ^
|可变参数前的第一个固定参数var2: d$ {! S8 O( V6 s% d
|可变参数的第一个参数
6 x. ]2 E- U$ U& a) O" a, s8 p|...3 Y' u0 q: X7 [; M% f( S
|可变参数的最后一个参数1 g; K8 E- A! A, a
|函数的倒数第二个固定参数var36 W1 }" w; ^. t" `2 V9 D( A
|函数的最后一个固定参数var4; q. A* |# B, ?! U6 [4 k: z
|...' r3 J' K" P# I; O- ]" H
|函数的返回地址' F( r7 B) U) U6 c/ j
|..." X' Z" S H4 p/ Y7 h# F4 D+ f
|栈底 高地址$ v) E2 g N; Y. U
9 r, u, |& r- i2)使用可变参数所用到头文件和相关宏说明8 c9 U5 e! o: ^0 o9 U6 K9 {% ?8 [
# _1 q9 S+ A: W7 G8 N在此,以TC2.0编译器为参考对象来说明.
3 n; u7 _0 w7 P0 T# r可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
) Q l" s7 A: O5 T2 F2 c# ?此文件为:+ V; C) z; k% T. M9 K" Q S8 k
/* stdarg.h
; Z x. Y( C; K4 o. ~4 |
0 B+ u$ ?# E! z+ }7 E0 SDefinitions for ACCESSing parameters in functions that accept V; `2 o/ G4 F: W7 q
a variable number of arguments.
/ x- [7 |! }% V( L, f$ t: Y
6 y. L: M8 v/ d7 \; pCopyright (c) Borland International 1987,1988
* U. L* Q' c/ }( l, L! TAll Rights Reserved.
' o# j% l( V( |) a! }*/
: B E' n) {) M& w; Y' A& M+ ~#if __STDC__
' \' z! `' H5 p0 u# \8 c+ D#define _Cdecl
: h! D# @4 _+ `0 J#else
: I- m; L* a( f6 N7 e) w#define _Cdecl cdecl
+ a% v; t- h! ~0 R* G4 [( ]#endif/ F7 L5 p; I$ |# B8 Q
$ C4 t! ? y! Y! |#if !defined(__STDARG)
$ K @3 W. y# J8 y! D#define __STDARG
, H+ A9 R7 O- T3 }" X" Y9 p+ X& A$ [+ M' C9 ?
typedef void *va_list;
& a9 Y8 G* h1 M; f" U' t! ^4 A6 W+ z4 D/ y% [
#define va_start(ap, parmN) (ap = ...)8 j: d5 O5 A" G4 h. k
#define va_arg(ap, type) (*((type *)(ap))++)
* d, j9 E% [% p( J4 C' T9 Q @#define va_end(ap)1 p/ q/ T0 L% |. Z' Q
#define _va_ptr (...)
* ^ p. W, g- C+ ]- M7 m' s#endif
, }! G" k, ^* \& y& i5 ]& Y. m4 K5 `# r0 P- M6 J5 L+ B! w/ J
以上为"STDARG.H"的内容./ K1 Q/ |3 A4 R+ j x y
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
3 b7 V2 d) i; j6 S6 z" {va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,6 Q+ A/ ^" f5 F9 @+ h C
parmN为可变参数的前面一个固定参数.
! \: @9 a& b, ?va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
2 [3 v! H& [2 |, ]' Sva_end(ap) 结束可变参数获取.& ~* y3 ^/ d" V6 w0 {
' L9 z( R! B) N1 P9 v- N8 f6 S3)可变参数的使用实例
# k/ B; ?+ i7 K$ a, @
. s0 c1 G- K0 Q2 U1 A实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
) h/ W' s7 s! b7 R- r* A% p5 K! r% u( F
#include<stdio.h># p$ o& l# |: ?( d. b
#include<conio.h>
( h# @: x7 ~) p5 Y- [#include<stdarg.h>
' Z9 N, K5 w; n5 X" ?. d- m$ I& nvoid tVarArg(int num,...);/*num为可变参数的个数*/
{8 H2 v( P1 H8 f5 n& t( P# Z6 H! r& W" oint main(void)
+ U. ~+ K( P. R: h{
1 n: c0 A* l: X6 R- G8 Uclrscr();
* L2 x& v0 ~: N4 }4 ctVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");% g, c, I- b7 ]
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");5 i- H$ v( E: L: U; ~2 B
getch();/ a0 S# f6 L8 v4 h7 Z1 c
return 0;( [% M5 ]* h7 o1 Q
}
* l7 G4 ?+ ~3 Kvoid tVarArg(int num,...)
P: o. z+ Q4 [7 L{& N6 T6 a* ~% g* U# G
va_list argp; /*定义一个指向可变参数的变量*/
9 \1 c d) J H5 v4 nva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
; x2 `0 ^% X9 D+ Cwhile(--num>=0)4 x8 q7 F# |$ \! h3 m$ [! W
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
, h; F) N- t1 e. f# L' S 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
+ _( r# a# {+ D" I* tva_end(argp); /*结束可变参数获取*/0 V5 u0 J2 Y) q' I! x; b$ K
return ;
; Y8 |, ?; \/ I( x0 L- E0 U8 E}
4 l% y+ r* J+ W8 ^0 O
! W$ d& n) ^, ~" V! Y4)可变参数的使用需要注意的问题% B) y1 o$ v G" L# O
1 d' s% z+ Z* y
1.每个函数的可变参数至多有一个.
( P# \0 {3 Y1 W7 V M3 h2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
! m! s0 J5 u+ w/ E b) S3.可变参数的个数不确定,完全由程序约定.
" V6 [( ~& Q6 K* U& k4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
; K8 c0 g* B& g& T' _& a. L6 F而printf()中不是实现了识别参数吗?那是因为函数
& j7 {( p5 l4 q: A) Z8 O: e% vprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
( s: t# p0 F, M: O" k/ M) `+ M' t2 x的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
/ _. v7 W$ l5 O4 R5 y0 Y4 X过在自己的程序里作判断来实现的.
1 u) L8 X7 {! ]! h! W3 ?) T5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|