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

函数的可变参数详谈

可变参数的英文表示为:variable argument.3 s) t" @5 O& I. O9 T
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.0 N+ K6 z* s$ i& B0 Y* `' U) F
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不7 s: ?0 e9 _1 ^  W# H
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
$ y$ N* g7 s! N2 N; q7 S! O实际的名称与之相对应., w5 ~6 W2 w- \# c
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
, z7 g: |/ [) M4 L3 N- \然而,更多地自由,同样也加大操作上的难度.
+ |5 ~# P( J2 }& L以下就对可变参数的几个方面作一定的介绍.
- F) O! N, q1 S9 T
" ]* e; ?  y! N1)可变参数的存储形式.
+ B3 P/ U4 z0 d3 R
+ d4 `( D6 U4 s大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
9 l2 H' o! P6 V( P- G6 l存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
% v6 R6 i/ w; K! W2 z; D: X/ J# M在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
" g; }' [- S7 C* w3 i这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.  P0 X5 n3 ]$ O8 d# n+ \% a* \7 D) @! h
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):: W! b/ P0 @% w+ X! C
栈区:- [0 g1 }. P: j: K% T

3 s$ g& a# l( O4 ]|栈顶             低地址% M4 x: \5 M! T! r, \

/ X. h  C& c6 j$ L* y# w' A0 J7 d|第一个固定参数var1$ j5 N% J1 f- w& I
|可变参数前的第一个固定参数var2
' r) f, Q' Q' V% }8 T3 P|可变参数的第一个参数
0 }6 p9 j$ z3 N; f9 `/ t. ]- ~|...6 o, `% x5 @5 a) i
|可变参数的最后一个参数
/ ~+ G  M+ f+ s$ S* b|函数的倒数第二个固定参数var3( ?) Y) f7 {) _5 ~" j. H9 n! m5 j
|函数的最后一个固定参数var4
# u3 x/ W6 M# e# p& m) P|...
# Z8 A3 s* d3 l' J8 [9 h$ ]|函数的返回地址+ t3 C6 L0 Z2 T0 [& _
|...( O- p/ u' R3 B) @
|栈底    高地址
% B' a3 ]5 Y1 e& U# G/ R8 h! E( l8 ], y/ ?, Y
2)使用可变参数所用到头文件和相关宏说明8 r+ ]' \1 Q( z/ }, [8 `

, V  B4 V! `; m! A, O2 W4 F/ \7 b在此,以TC2.0编译器为参考对象来说明.
! F! }: _: L7 n5 a7 s可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
+ O. V8 z8 d4 b5 q/ z此文件为:
+ O5 K5 X2 m) I4 r/* stdarg.h  v6 x+ d3 ~4 f
9 i, q" n8 b# |4 c+ @. L
Definitions for ACCESSing parameters in functions that accept# H, d- S! J4 g9 ?4 I6 i, B
a variable number of arguments.
- ~, A; E0 L7 h/ c& r$ \+ m* ?
* Z# F" j7 A! G4 Z: }; d  M4 XCopyright (c) Borland International 1987,1988
! ^, O' `' e) OAll Rights Reserved.
/ L9 I; Y0 o$ N4 n* g, }3 s( N* E, s*// j, R% u$ e4 E0 n3 N
#if __STDC__
2 |& j7 l  p8 Q- l1 `$ N5 L( S#define _Cdecl& u9 q; D! p$ t: X" y2 z
#else9 x: ]' _/ i9 w
#define _Cdecl cdecl% t9 f( d5 x3 D9 W& l
#endif6 C; O% i* v7 H/ _
. P) O& m+ c5 C3 ]6 f
#if !defined(__STDARG): t) f4 z* E/ G! X+ Y4 |
#define __STDARG
, o5 y8 V: _3 N- \/ Q
% a7 H( j* h! p$ |' ^' xtypedef void *va_list;
4 T! i$ z$ i0 K* T. N/ k9 S4 B/ K# ^( x* W; Y. j
#define va_start(ap, parmN) (ap = ...)1 `" b- W0 f/ f, K! @. B* @
#define va_arg(ap, type) (*((type *)(ap))++)1 }9 Q7 r% j1 N
#define va_end(ap)( ]) M  K# P5 `" E$ v
#define _va_ptr   (...)
. Y: q  {1 p$ _& R% N#endif9 o! O# k, @; m8 t2 u

* x) ?( ]' Z2 s/ H以上为"STDARG.H"的内容." h5 I$ X. {; y8 p+ t5 I, P* W# a+ k
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
2 `$ U3 J. K( e5 G# nva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,- X7 m6 s+ _6 S7 \8 r8 n' t
parmN为可变参数的前面一个固定参数.8 P9 U  e" H- ~) H
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
/ ]5 \( _' q4 j4 ]: |* f2 n  Y2 Q$ Mva_end(ap) 结束可变参数获取.) u( b. D4 h# x3 G2 _$ B+ v

& ^% h4 W1 x" B3)可变参数的使用实例" w. V4 L7 H  @( f& S1 I- f& A
4 v) U5 F2 p  u; [6 W2 Y$ E
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
2 G9 V" t* ^! c5 y
9 F- s' c' {& k5 f#include<stdio.h>
: ]' ^0 @  [$ `- E. C1 I3 t  V9 e#include<conio.h>
4 u( u2 v; y- i( J: s8 Q4 j8 u#include<stdarg.h>  w% ]$ W' ?) ^, F4 E
void tVarArg(int num,...);/*num为可变参数的个数*/6 G+ L; u8 }" e% r9 v$ V" l
int main(void)
2 L! @% p2 m4 h" I2 s' q( M{( X$ p9 B& P/ R. f: h
clrscr();
3 T7 i- O% U1 R1 o5 mtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");' u+ }1 b$ _8 y' m+ E* N/ \: E: r
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");7 S2 h% k2 m# j
getch();
( G9 T! [* |! U$ m, |5 oreturn 0;  f: a% v8 F$ t
}
4 h3 Z$ ], T) p9 V% Vvoid tVarArg(int num,...)
( N9 Y" h$ l! R- {* C{
3 I8 A! A: u# Z( l; Sva_list argp;  /*定义一个指向可变参数的变量*/; ~4 v9 G* m4 w& ^
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/; @" q8 D% C; V( }) E
while(--num>=0)/ G0 d* \* L" q) l1 s7 t! G% g" ]
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
2 q, a& C  u7 e  I2 u9 V    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
; ?. ~: [2 A" f, K2 [va_end(argp);  /*结束可变参数获取*/
; `+ I; M& z, {1 {- ~3 Yreturn ;
% A7 H# V) O$ j. ^}9 k' V  M2 U2 }" e  ]* u" q/ Q

& S% e5 ?$ |8 _, C8 U& v2 G5 O4)可变参数的使用需要注意的问题9 l+ a. N8 v5 r' e0 ^  r

& D' ^2 j7 G/ H& ]0 }  v1.每个函数的可变参数至多有一个.- q  D. z  a$ k
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
" r9 O9 r# `# S) G3.可变参数的个数不确定,完全由程序约定.
2 s& Y( B0 T  Q4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.& h7 r1 _; ]5 c1 v
而printf()中不是实现了识别参数吗?那是因为函数 ( j4 E1 L: d, ]! Q5 N
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
- I9 L0 e' ^6 A+ Z/ e的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
5 I) {$ F7 c* Y& |9 l, g0 ]. S过在自己的程序里作判断来实现的.
8 l5 O* ^! v( D5 n& Z5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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