获得本站免费赞助空间请点这里
返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.$ z) Q3 h; ]' U
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
1 f3 f0 {+ N% a5 D! }% q3 Q% s$ v! u可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不% k. l6 M* i) H) h% h" c& ]7 T
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有/ L9 d7 _: _. |2 ], }
实际的名称与之相对应.$ x9 L  ]; e# G% C. Y2 S
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
, H: H) O; F2 q3 n7 L4 m! I5 q然而,更多地自由,同样也加大操作上的难度.
: R- Q. y& _1 X% g1 w以下就对可变参数的几个方面作一定的介绍.
% V! q" o" W0 k& z2 \! l' L9 [/ {8 w8 m* O  Y0 d# }
1)可变参数的存储形式.
  x, q$ Z' N$ k+ D5 u) M4 _5 H2 \0 @& m
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
* g8 p9 }  ^- D' B/ d5 \+ h; x7 d" c3 {存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.2 c4 }% u0 h$ m; {  u8 j9 s
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
! t& o  v- V2 p: O这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
; k7 k7 {0 u* s+ }: _8 r因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):) z1 M2 g# d$ E3 t9 C$ [
栈区:7 f  ~: q$ k8 ^4 w
: r' X, ^& N. `# B6 g# _# t4 `6 d
|栈顶             低地址
* ~/ h7 l- p0 j  l( v  H" N5 e6 i. U8 m7 w
|第一个固定参数var1* v, E1 R7 C" v0 F
|可变参数前的第一个固定参数var2$ @$ [4 h2 t+ r" e3 {3 w+ \
|可变参数的第一个参数  F9 X  q! \1 p0 Q4 T- I
|...
0 M, b1 B8 f% c1 l. C|可变参数的最后一个参数" c2 L* E5 }; z8 v
|函数的倒数第二个固定参数var35 Y: q. n- G% b
|函数的最后一个固定参数var4
1 |: \) w; z- S" ~+ q' @: ^$ w* U|...
2 d: K7 N) X" z; w* p  G|函数的返回地址- s  r1 r* W1 i* S
|...  E# `( H; q" S% n$ f7 t( L! N2 v
|栈底    高地址# G$ g( b' P: b
+ P; n$ B7 x( [5 J
2)使用可变参数所用到头文件和相关宏说明7 K6 C7 l& _. ~( l" p4 |, l' T
9 Z$ d, [6 h0 H- E5 s* T8 V
在此,以TC2.0编译器为参考对象来说明.( N( Z2 P/ k' N. z( J2 }
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.; x- A& V/ |$ y7 o3 p
此文件为:
8 ~: S9 l4 u# H' s, B+ ^6 v4 v/ D/ M; k/* stdarg.h( u1 y- H8 `! B9 n. o

8 }. ]: v7 }6 @" K  ^, oDefinitions for ACCESSing parameters in functions that accept! [7 e0 o) i" c  w# Q6 _* A  v
a variable number of arguments.
0 ?* [" a3 O4 ]9 Z, B6 Z) I6 _; F
Copyright (c) Borland International 1987,1988. k. ?  X% Z' @. P4 r5 _
All Rights Reserved.
3 Q+ K; H  s9 R# ?* c*/# z* ]+ Z. O6 l0 T
#if __STDC__* E* X7 e; U" Y; f! }9 ^  b
#define _Cdecl
9 Z7 M. N+ s& [. N% k9 w) i# V  ^#else
  P6 N. R2 f& e/ @1 F- L+ p4 E/ T#define _Cdecl cdecl' @0 E$ q% W/ n0 a
#endif
' \1 c' O# X9 W
1 v# @  P7 ^) c. `0 X. J' t  [#if !defined(__STDARG)7 N+ v$ f% f; I$ W  ]8 r
#define __STDARG
, l7 [$ A5 [% F) q( S$ B
6 X9 N$ |: Z: f7 e2 \$ utypedef void *va_list;
/ O) J2 D1 i/ F, X: W+ v; u" B3 h4 Z  m. ]
#define va_start(ap, parmN) (ap = ...)4 n& D4 Q& V  q0 O& f9 t- r/ o
#define va_arg(ap, type) (*((type *)(ap))++)9 ]5 P# }" \' G/ _" u8 W) b
#define va_end(ap)* C8 |6 L, a- y5 U- t# m
#define _va_ptr   (...)9 o% p7 N5 f& y' G2 w# T
#endif
5 A6 _* j  M# x. M6 _' u
- B, u5 \1 G9 P/ \4 W6 ~- ]以上为"STDARG.H"的内容.
/ k, |$ Q7 l. J# m; _/ j6 F该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
# \/ e4 l2 _% [2 a: ~7 ova_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
8 E6 z, n" [; Q  r( LparmN为可变参数的前面一个固定参数.8 C( T' F! T) L5 q% A. p9 q3 C2 }6 M, N
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.; F, \+ k# V) j- E
va_end(ap) 结束可变参数获取.
) s0 i% N! ^: L" f  V  Y) J, r: G, b" H6 k
3)可变参数的使用实例
& m$ {+ }; K7 k- D$ [' V0 ]( |: j5 L0 N1 ?) L
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.8 J2 B4 c) t2 d1 L
% I, T/ s0 P2 Y! L! ^
#include<stdio.h>
) `$ c, ~3 b% m#include<conio.h>
. V& n$ J+ R- F#include<stdarg.h>
6 m, g' b1 p# H4 _+ `void tVarArg(int num,...);/*num为可变参数的个数*/# ^1 f( u- p% c
int main(void)
% U1 X2 j/ d. n% ^{# n; C" N# ~! n
clrscr();9 c5 x( z5 O% t0 O9 G
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
7 M; J' h! R0 j6 qtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");' Z  A/ W7 Z% ?4 K2 N
getch();
. Q, J8 T  D" k$ [return 0;5 I  F* C, A; A5 i( x8 s* K
}# \6 U. \4 k: K6 o3 R: Q& ?/ w' x
void tVarArg(int num,...)) ]+ u  \# g, B
{
2 M  w* @# \- e% H( n5 }+ `5 ?va_list argp;  /*定义一个指向可变参数的变量*/
* k$ Y! @+ b6 Xva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/, E# M: F, _! _7 v# n
while(--num>=0)7 o: E( y- E, }5 e" ^" I' v
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,; b5 b$ T. M9 j' f* j
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/. l2 m0 @, V: r+ ?" u* Q( M
va_end(argp);  /*结束可变参数获取*/  i& z4 L* i' a7 v1 [
return ;
. }6 f/ ?  b. f}
0 t. e- s* S; X* h0 @8 Q
* _$ a9 c/ R. K1 O# o$ k4)可变参数的使用需要注意的问题6 P+ _' C& |+ D8 f1 V
7 s2 U3 c4 z: I. ]- w- D
1.每个函数的可变参数至多有一个.  w- B& W- J2 h5 r' f" ]: B6 @5 K
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.& A8 o3 p" K+ w7 e9 Y
3.可变参数的个数不确定,完全由程序约定.$ K5 C* W# g+ I' F; b$ K3 f; _
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.) w. \" q# p4 O. y
而printf()中不是实现了识别参数吗?那是因为函数 & t) _, [; G+ H5 ]% s& P3 }6 W
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
, M* m0 N8 R3 f5 w的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 : Y" Z- o; q2 ?. N. I; X/ k
过在自己的程序里作判断来实现的. # Z' o& t- X/ L: g$ b+ m; y
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

返回列表
【捌玖网络】已经运行: