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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
( m y' x6 C5 T+ U& \- Q4 Q它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.0 k# l5 P, B4 @; m) Z' {1 B
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
2 k; h, I! | b( j定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
) L4 @& S$ Q: Q! ?* j3 W! s实际的名称与之相对应.3 D o- E6 X( V6 q
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.& x0 o' ]/ m( T( f) Z
然而,更多地自由,同样也加大操作上的难度.% P# e7 f3 @ R$ ?6 u) D0 Z6 f
以下就对可变参数的几个方面作一定的介绍.0 X" }) n0 ]" X
9 ?/ a6 m% ~" u+ u; r$ K( p1)可变参数的存储形式.9 ^" j( u/ X! O" t$ R3 n0 [) w
* ?" \( |1 j, [* g1 s( ^
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
9 [8 z8 G2 L. z% ]" @存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.4 [# i* k: s: E! s$ F
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,( u3 g* ]) o, W. F3 T+ y+ s# i
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.8 q% j! _7 N" g0 s7 v- p
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):. W4 L& v$ S" c
栈区:
: R3 x" S3 u0 }5 @3 p
+ S' }9 g/ T. V|栈顶 低地址, O; d4 z% M0 n1 T9 U% C3 x4 n/ w
- `4 f+ k* D) S|第一个固定参数var1# Y% Q' d, Z6 k' z3 f& c' H
|可变参数前的第一个固定参数var22 @, i t. |3 g
|可变参数的第一个参数6 [" j; \7 K. S0 |3 f
|...6 T3 g4 t1 o k% R
|可变参数的最后一个参数
& V4 H/ J/ w( D% t) n5 L|函数的倒数第二个固定参数var3
8 k! ]1 {/ s6 A|函数的最后一个固定参数var4
. h: e; G5 s+ k" f|...
6 u! N& M K* x1 ~|函数的返回地址
; A/ D! E& c! H1 i2 _+ I|.../ f% f" P- F$ j
|栈底 高地址
7 q2 m1 r3 M* K+ `/ W" B; A
# p0 M- G: W2 w: R) @2)使用可变参数所用到头文件和相关宏说明3 [1 A4 F8 p. a
$ N( w) v! ?5 h3 s. E. J, Z在此,以TC2.0编译器为参考对象来说明.' T8 m6 R4 D, E4 k
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.# k# `6 K2 x( }' ]8 v4 s
此文件为:" M+ {- L/ g/ L& i
/* stdarg.h8 H/ h6 L$ u5 N% R
: F, X: x- i. x& d+ s9 R
Definitions for ACCESSing parameters in functions that accept
" Y% X+ @8 U' @' {a variable number of arguments.
1 i6 T9 w2 B/ |1 r# f! u( \% \" Z5 j/ N7 ?
Copyright (c) Borland International 1987,1988
8 U0 d+ [* p3 o* ], ?6 q) [! D# hAll Rights Reserved.
; c5 [ `9 W5 R3 D; I4 s2 C3 a*/
: R6 f' E. m. v& t% z% S#if __STDC__
% O- s, g0 ^( i, ~4 _ |0 c#define _Cdecl
& m: f) q/ l$ c& C. a, Z( e: ?#else1 I' E% w$ C" p" J6 ^1 I% y
#define _Cdecl cdecl0 P2 ^( A3 s1 o
#endif
% }, h" Z" U! ]% A+ p! H$ E: P) M8 ?; B5 S. P/ Y( E
#if !defined(__STDARG)$ [; q; [4 _7 ~
#define __STDARG
; V: W8 r+ }. U" M9 n- X0 ]! O4 |; n* A
typedef void *va_list;3 [4 z( {( M4 |& k- d& ^8 N& t
* \/ ^5 q) _% Y+ B {#define va_start(ap, parmN) (ap = ...)
) U: Y2 K' i9 S" T; p) w! k& V0 W/ S#define va_arg(ap, type) (*((type *)(ap))++)
' `! k4 z! e# s#define va_end(ap)
% A/ E. @, H: K1 l/ A8 X7 M5 t#define _va_ptr (...)
+ t9 E- N% r+ `. M4 s' U. k#endif! r+ i Z% |9 `& C. j: p/ I
! O4 E' [' P% i) K! ^: _7 y; E/ {以上为"STDARG.H"的内容.8 @! ^) n4 I8 T. W6 x' U+ O
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
; a' u- i2 j/ C( c" uva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
2 _; [) _9 d: o6 c. x2 zparmN为可变参数的前面一个固定参数.
2 T$ \3 H8 i6 ? t0 ^4 Rva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
7 B1 q( d$ [9 ], ?/ w* ova_end(ap) 结束可变参数获取.' S8 T3 P$ U+ V+ C( q5 L8 z3 Z
" J/ g; r: G8 ^7 w/ O: N2 x
3)可变参数的使用实例
+ n6 V9 J" n) z/ i* y) u. }/ s. e6 o9 N
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.; H; Y7 x- f& `( d5 D
: W: W; a! U# w+ F Y$ T
#include<stdio.h>
. g1 y3 T1 \" i9 \' p#include<conio.h>. b6 X& T' q& j; [& h# M3 T$ T
#include<stdarg.h>6 {9 l6 e9 `, [
void tVarArg(int num,...);/*num为可变参数的个数*/
[) B3 D8 ]5 }- o7 [, eint main(void)
0 W5 ?, X- L' x$ p4 r% ^8 t7 L{
) q9 u+ n& {% m$ p; t& v2 u$ e1 Oclrscr();
7 X7 J7 V3 P2 ]! ptVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");% e% G+ _ K3 W% h/ q) P% B
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
6 j1 O$ A) l5 G2 Jgetch();7 L, O6 Z4 H' L0 }. J! B; y
return 0;
! R$ o5 v9 u1 d1 Q+ Y}& u6 b+ K+ s9 R1 I5 D( b+ G! ]6 F
void tVarArg(int num,...)% _% M8 ~" k9 D
{
" E# ?7 e6 \1 x" u! `2 T, e ]va_list argp; /*定义一个指向可变参数的变量*/8 O6 p1 B. V1 U) N
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
2 D2 I, J& F b& D/ @( c& rwhile(--num>=0)1 y2 k; q2 X$ X" n: @/ o7 v
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,/ J. I4 ~3 ]! ?4 _( E1 _
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/: W. h$ o. i! i5 g) N
va_end(argp); /*结束可变参数获取*/
. a4 V G' W: r/ Q+ _% G$ Greturn ;: p% P- h2 i* x+ c9 V
}
6 h0 N* N+ Y3 \- K( R5 O) Q+ f& V' O1 H) b: M4 [
4)可变参数的使用需要注意的问题
7 Q, p! R, ?* t9 p; o
8 d3 e% _; u m' ^7 Z1 j8 N1.每个函数的可变参数至多有一个.
! v8 L5 L; n7 a5 M( P! s* ]2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.9 E, y. I A. q
3.可变参数的个数不确定,完全由程序约定.
( n! I0 o1 _7 Z, N/ l4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.$ }; D* e+ I+ W1 }5 \
而printf()中不是实现了识别参数吗?那是因为函数 , k8 d# J- v$ G" j# ]( W3 K
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 9 n& H; s9 ^6 M' @- C2 ]
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 ! z l: |' D1 b" b* k
过在自己的程序里作判断来实现的.
9 ~+ E; {+ e9 I( s5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|