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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument./ S5 ?; u* O g
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
# U8 T$ o/ G3 p8 T# z4 t可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
- Q+ d$ ?5 q1 \: h& r* r; P: a) o定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
+ a3 U9 M$ H2 G2 j0 K实际的名称与之相对应. r2 {5 ?- r/ h$ H" i0 a$ _4 g, h
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间." x# [9 l4 q3 m9 s7 v" R8 F! B/ X
然而,更多地自由,同样也加大操作上的难度.
6 f7 t( r4 ~: T2 Q$ j以下就对可变参数的几个方面作一定的介绍.
" b& n( i. T) Y2 d
9 w# A+ N0 C* ^& y1)可变参数的存储形式.
% G4 o- ?' y4 s! \; F- i$ b
- c7 `$ W* ]: ?# @3 |2 t大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
3 e% i; J! Z/ g+ Y6 k8 ] j+ S" Y存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
; a5 M& F; c7 @& ~' I$ i在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,: v8 t( \ H! f3 i) a. b! Y
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存. `3 z- d6 L3 v7 {. M
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
& S- d* r# k, D+ B" ]' C栈区:4 l. H1 F) D0 Z; E$ Q
+ S" f# d) z9 R1 B& h& m; U i|栈顶 低地址& F% z( c5 u* e5 u1 K
0 ^ g% U1 N u$ d( L
|第一个固定参数var1
2 d- j1 n* i& {|可变参数前的第一个固定参数var2
; ]2 W' I+ y: V5 x|可变参数的第一个参数8 Z1 v, V7 M5 l( k# a
|...( B9 g. @1 A% }8 W
|可变参数的最后一个参数 w0 t4 U2 z4 a* y' l5 S
|函数的倒数第二个固定参数var30 q' R' t9 s+ K/ C3 ? Y# Z9 p) i t6 C
|函数的最后一个固定参数var4, R2 E* G* T. d; M3 L2 B* D
|...
' N; d* R3 l/ t. [$ h' b|函数的返回地址
1 i% R* e g% z9 {7 U|...
8 U! f3 ?, w0 P! x|栈底 高地址 \; ^3 i( [. v. g4 Y
, Z' Y3 v: W8 e+ R; m
2)使用可变参数所用到头文件和相关宏说明, E' v8 ~; p! V& r _
3 ]4 j m3 p. m; n/ l0 M7 w5 c
在此,以TC2.0编译器为参考对象来说明.2 I6 {) b. q4 F$ h! l
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
! d! G% N% e" q; Y T( w! t此文件为:
, I' R: t) s5 m5 v2 G% l/* stdarg.h" r; A9 K* |# W8 U; E
2 E5 L3 O9 J- d9 x, w$ W \0 \Definitions for ACCESSing parameters in functions that accept: P% O! C5 G) w# ?6 H. r7 K/ K! }
a variable number of arguments.
) E* J5 M0 c& O: E) z; r# P
) @% R! u1 _ QCopyright (c) Borland International 1987,1988/ j! B* \. B7 y2 J# Z. C% i8 i
All Rights Reserved.
0 O- `( U, o, g$ j: o& q) q*/
! ` U% _7 x2 }# m#if __STDC__! c, u& ?8 D* P
#define _Cdecl! s; }1 z+ X, A5 v8 d! u
#else0 ?+ \( V/ f! L4 O( ~$ O& q. S# W8 D9 ~
#define _Cdecl cdecl8 E4 g; {; m" [ B! Y
#endif
! M6 b1 ]6 x2 ]2 d# f
3 B) Y* l' |! W# q1 z#if !defined(__STDARG)
+ ?- M& y3 s3 m% c; ]#define __STDARG
" P% f9 M9 B7 w: s: q; v
7 M, [& K# V* u! T1 ]4 a1 ytypedef void *va_list;6 U, F4 |2 h# \) [# d! i% m
( Z5 l2 Z4 V( _
#define va_start(ap, parmN) (ap = ...)
5 A2 ]) J$ e- x. A) O" \+ q#define va_arg(ap, type) (*((type *)(ap))++)3 y8 n+ C0 t/ Z" b- t9 b
#define va_end(ap), X2 k! p6 e& q( `( d
#define _va_ptr (...)
A, B; Y( z& w3 p2 x#endif
' J: Q0 [8 _% v' m; a
+ I# ~/ n/ e5 R9 H- c以上为"STDARG.H"的内容.
/ g# M/ g$ X# K' t9 j9 A9 a该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
: F4 \: [; e, b5 z2 @$ u4 p0 Xva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
: n( y, N- c0 Z! Q2 a9 H, fparmN为可变参数的前面一个固定参数.! z( J' h; i& x# L+ c- ^. U7 v9 j$ T
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.6 A) n7 i9 m% _; s' e- f/ v7 a
va_end(ap) 结束可变参数获取.; i/ s% O9 w% T# w
a. v" p: F6 @+ }3)可变参数的使用实例
* X* G/ O3 }+ y- S, G2 d8 ?; Z+ G. R4 \, Z; u
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.5 i. a' t- H: S. l. W
( I( s4 P" ^4 b! f; c#include<stdio.h>
- }# ?( s, r5 F2 k( f, o#include<conio.h>
- E3 o/ N& k6 M, K% [; M/ j#include<stdarg.h>
0 \ E; @. L. n, Jvoid tVarArg(int num,...);/*num为可变参数的个数*/" ?1 {- E0 q* a8 W% O7 B2 N5 Z
int main(void)
. f* S& n# k9 N7 I) |% v{
: _( ]7 R9 V) Oclrscr();
2 ~9 o8 b+ I( ?7 u0 BtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");! T/ V5 i6 `1 j! P, `# e. q c4 `, `
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");: I) ^+ K2 Z) F
getch();! u5 } B/ P* h) Q: q
return 0;6 n( Q. g' s# x, V- N7 g
}1 s& b/ T: }! ]- a% K B6 W
void tVarArg(int num,...)7 |/ }) ?$ h. f4 B0 W. E$ ~
{
' U9 a! P7 i5 i( r yva_list argp; /*定义一个指向可变参数的变量*/
" B% O6 _3 g) y, d \va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/2 b5 ?7 O2 n" ~9 J1 z* m) C9 X
while(--num>=0)& e2 U- Q |# b1 E
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
1 E* V" {$ |9 p7 D) M! v 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
+ m/ T- {! E g- B: l6 `) i2 ~va_end(argp); /*结束可变参数获取*/* w* }4 H4 ?0 x/ j$ N
return ;
# Y# N# C; j, Q}& S7 m% V5 c% P% ^% I- B! g
: J# D6 V- k2 m& U) i4)可变参数的使用需要注意的问题. t2 m+ i4 k& {: f! `: t! |: C
$ l9 A. x# U% g4 Q. r1.每个函数的可变参数至多有一个.
6 r" b+ W( y. b6 I9 H' B0 I3 n/ I4 _7 X2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.: O$ }! D& Z+ x. B
3.可变参数的个数不确定,完全由程序约定.
. |6 m4 o, [6 @' p, ?4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.9 e5 C; |4 Z: _- e" y& R
而printf()中不是实现了识别参数吗?那是因为函数
/ G9 b- a o }$ u9 v0 u$ _8 Xprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 7 }1 K' s: L" C7 ^0 b
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 , x9 E! T+ T! p0 {: M* c" L
过在自己的程序里作判断来实现的. 8 I2 Q6 ?) M' ^: s$ z! C1 O
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|