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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
/ C6 a$ x- x# S! [: K: a它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
8 b) t( K4 X3 \' z% J* l* l% Z- J可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不0 M! D: ~' e: _$ C% }: }- h
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
" O4 C+ u0 A% ?4 z8 I! t) q6 f实际的名称与之相对应.
' C% _/ u" {1 p0 x/ |由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.. z2 B" B1 G9 y" A8 Q# _
然而,更多地自由,同样也加大操作上的难度.
4 P* v. v8 W2 |) A* z以下就对可变参数的几个方面作一定的介绍.
% ~" _' E4 H- C4 l+ J& }; }
# Y! n$ e3 |) n/ ~' D1)可变参数的存储形式.& M6 s; \5 d- p: z+ {

0 r. E5 a2 l5 G5 w大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,& W, x5 z6 [% w4 _- {; `* A
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.% O/ V. X. b& ?; d( @
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,9 R3 V( z1 @8 n
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.' ~1 k% V0 g8 r7 f6 w7 o
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
* l& ~) g2 t% E: _3 p8 R+ i$ t' p4 c7 x& m9 b栈区:6 ]) L3 \5 L- H4 K# t

6 t" R" W2 L8 R' q. J* \3 @' N0 W|栈顶             低地址, `$ v, m* G0 |  U7 l

9 R; G% U* C2 N6 ]|第一个固定参数var1$ T  I6 W6 Z, k: Q/ U+ d6 h
|可变参数前的第一个固定参数var2
9 Z' X5 q1 c7 u. N( M& p; D|可变参数的第一个参数
3 R) w: p2 ^9 @|...; Y( ~. w1 p) h. ]
|可变参数的最后一个参数/ Q' k/ s0 f3 d- i0 L
|函数的倒数第二个固定参数var38 ~) r% s" ]+ a. e
|函数的最后一个固定参数var4" _$ M# s4 w9 P5 W
|...
0 ^% a0 t. k1 o|函数的返回地址0 A7 C- x, C+ N2 W: |/ d9 c/ V
|...# g9 ?: `1 G0 c0 O, `" d
|栈底    高地址& E& D1 ]! x7 c2 J8 U' V9 X

- `( S0 O3 B( b3 l& L$ v2)使用可变参数所用到头文件和相关宏说明: f; `  \5 ?" P) d3 ]- e
4 k2 f0 |1 k1 X6 W
在此,以TC2.0编译器为参考对象来说明./ O2 [( u( S8 y/ w& w+ g4 N
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
9 ?9 B5 B/ T( t* }' @0 L此文件为:( m1 C) w& E" X" b3 c; r
/* stdarg.h
0 S9 ?0 Z6 ~: n) K$ N# G* }3 C: o6 s* k/ C. P  s
Definitions for ACCESSing parameters in functions that accept4 m" {/ i6 u. J$ t& y/ e
a variable number of arguments.0 @) [& }, M! g+ h  h/ h  o* X" i
2 B' b! I1 Q) p/ R( _) k
Copyright (c) Borland International 1987,1988+ H% Q0 g" b! C9 P% R. z
All Rights Reserved.! @9 `0 ^1 C, z# ~* V
*/% Z9 m0 C, c# L% T- f. C
#if __STDC__
' {5 ^  B( d: g/ n7 V5 [#define _Cdecl
, J% Z+ a% T1 \8 N* k- d1 M#else  B* }. C) z' x9 q4 Y3 Q# O9 P
#define _Cdecl cdecl
7 `1 m; h. R; q0 H3 i#endif( Q$ m5 [; W' p* u) T

# \- ]. b4 V: M9 L, V$ O( G$ U2 Q  W#if !defined(__STDARG)
5 O, F5 F5 K9 ]/ N6 L#define __STDARG5 ^- g/ ]# l. u) D0 ?2 n
9 O. t2 E9 ^/ Q
typedef void *va_list;
- m- p. l) I* T  j0 C1 ^9 j' v" k
2 ?3 u) f  d% \9 \7 m! L#define va_start(ap, parmN) (ap = ...)
1 k' I: Y7 e3 l# T) ?#define va_arg(ap, type) (*((type *)(ap))++)1 i9 ?8 `! P1 V$ q; [$ J  r
#define va_end(ap)
6 r$ f: `( s0 ]6 ~#define _va_ptr   (...)
9 B2 g7 F. r( r+ X#endif( }) j5 N  X7 B+ i% q8 P
3 A1 F* ]/ D- b  p
以上为"STDARG.H"的内容.
2 x3 y& Q* @  h0 t! l/ V+ [* @: ~. G该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;$ N1 O! @- U0 f9 N, d1 h
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,( D. v  V+ N  P  ]8 A3 W, {) @/ n
parmN为可变参数的前面一个固定参数.
0 a3 K" Z# h% G( Dva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.1 d$ n; b6 |3 F* q; l- N
va_end(ap) 结束可变参数获取.8 J) _/ S5 A. f2 X, |% v5 ~- y
8 M( j& H8 O6 `9 C2 Q) e
3)可变参数的使用实例: m/ c! Q4 _5 i/ x
/ A& A7 }" c( Z
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.9 U, N; r" ~7 p% L
! T+ l" x5 o9 R* Y
#include<stdio.h>
( v& z! k' i0 Q7 ]8 ^- Z6 u2 e#include<conio.h>
% a+ s( X! V3 X/ A#include<stdarg.h>
0 q: y6 j# K! o1 P/ |: s5 Qvoid tVarArg(int num,...);/*num为可变参数的个数*/
: g5 _  |  O, Z1 s" Eint main(void)
6 o4 u5 s! ]% F; b& v7 A3 s- |: H{
3 l+ E  e) T4 }% I3 V( D5 bclrscr();
+ V+ e% p4 w4 F9 b2 N+ xtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
1 o+ S5 g3 q9 h& TtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
/ ]' c1 C. Q& @8 x( ^' pgetch();1 q: \$ j2 z8 [$ f* x8 G6 w
return 0;
; G" _! p) @: O- Z}( w! ^* }$ R1 z- W( b/ d
void tVarArg(int num,...)
9 |. j8 a# c% q" m0 x+ f: M% H{
, G, F! t7 p- M3 O& W% q: N9 t* Gva_list argp;  /*定义一个指向可变参数的变量*/
% f; _; ^/ g# r+ b+ I& e; A$ |va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
7 \- C" }  }  I2 ~; a' W, h, n% S; }* e/ bwhile(--num>=0)% p0 V: }6 [' u, d: W" B* f
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,- F: t4 v, P5 U8 S
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/* e+ ^1 s+ ^# |& i+ o, r
va_end(argp);  /*结束可变参数获取*/
4 Q& M! K9 L( t: B  }9 Z0 Kreturn ;
9 m5 d. G3 [  \* z% V}/ ]% r% e2 H! T+ I: W- r0 D2 W

  ~/ Q: h3 |- P& V5 m4)可变参数的使用需要注意的问题+ {* O- A; }3 E

3 k. X0 A" L! x. [; o1.每个函数的可变参数至多有一个.* s0 _( i* E* c$ \. i
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.  {. ~+ A6 o+ k# ~2 ?' c
3.可变参数的个数不确定,完全由程序约定.
/ H+ M5 J, _( ^1 Q4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
* |, }& ]( V8 j! f; n) e而printf()中不是实现了识别参数吗?那是因为函数
! v- O" \* ~6 `; _printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg , g$ U8 Y1 E1 P: ~9 Q8 N
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 ; u5 O5 b4 ]) Z, G2 `8 X
过在自己的程序里作判断来实现的.
; M8 u# q# |4 J/ ], F5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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