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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
$ w- d6 g; k* P. U它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
' a$ Q$ r+ e) M$ A( L可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
! {& b) `# V5 n5 e* _定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
3 f9 |( E- j0 \( y7 a9 u7 b实际的名称与之相对应.
: P1 H2 Z  \8 `由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
9 M* m% O, q$ A0 A  X然而,更多地自由,同样也加大操作上的难度.7 Q- Q! e  N. J4 C
以下就对可变参数的几个方面作一定的介绍.
8 w- g0 ?. @+ H2 C9 T4 o2 H' _# F, y& c
1)可变参数的存储形式.( u6 B+ D" Y1 Q. L8 O
# z/ H2 M, W/ E  ?/ H4 W+ o
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,+ H4 {' _- m. r8 m; K
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.! y, r. p& {% t7 a0 u/ {/ U
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
: G4 C* O/ C9 |- Q  j2 D7 k- `这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
1 c( F: @* R" r( }: [1 U因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):  q( m* Z: h! a9 V# A
栈区:
+ Q, x+ }1 o' w, f+ P, h2 V& M: ]" g3 c) d2 z+ y) N8 [" `. C
|栈顶             低地址" u) a0 ]4 ?( V/ u

8 H4 B" A& j5 P! x1 R|第一个固定参数var10 Y4 a. \* B; K& ]8 }# j" N" ^1 @
|可变参数前的第一个固定参数var2
: V; Y9 e& z: \( S$ e- d" v8 b|可变参数的第一个参数. e+ ~( k- f3 o0 _( e; n
|...
+ U! _9 ]# A2 M9 }5 ]8 O6 `|可变参数的最后一个参数
. C( e2 P% n) R. }! H* f|函数的倒数第二个固定参数var3% j0 x& n* g' f6 ^% ?
|函数的最后一个固定参数var45 {4 N9 Z5 m9 N
|...
" K  r' a* m0 H7 I|函数的返回地址
, t% Z# |" o3 H|...( V, C2 ?2 l$ e1 f% G* M
|栈底    高地址, o1 o5 v- d+ X" L
  M6 o+ c* l: c" a5 o! T8 A! A. r
2)使用可变参数所用到头文件和相关宏说明' y% s4 k% c& j9 f0 ?* @

8 ~( Q, d# @0 K( Z+ B' j: w/ b/ q在此,以TC2.0编译器为参考对象来说明.% o! `: ~( ^9 ]& k
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
, M" l' _! p; `此文件为:& M5 U) g9 S  @! G; C
/* stdarg.h1 J; e' W+ l9 o. Z( a- x
! ^* ^2 q% j* Z7 b
Definitions for ACCESSing parameters in functions that accept. `3 @, ]1 a. u$ L/ w8 T
a variable number of arguments.6 e& J$ w- K7 |$ M  N: \/ U3 M. ?

9 z4 }* B6 G# Y. lCopyright (c) Borland International 1987,1988
* C9 ~/ M0 H/ E( V, PAll Rights Reserved.
4 ?3 X, Y/ I7 H  F8 g$ D*/
0 s% ?$ n8 d: d6 K: [& Y#if __STDC__, T* c1 C, r/ a( A/ Q
#define _Cdecl
0 s8 a: a' ]% Y  O#else' j  L! a6 W; z6 I% J; U* Y  h
#define _Cdecl cdecl6 d9 x% |( ]2 R# i) e$ s
#endif
8 F4 ?2 Y" Q4 b. K" V) J0 e8 C+ s; t7 T2 C5 c" Y* I* q8 |, U
#if !defined(__STDARG)- f" e1 D! M( ?' p6 M
#define __STDARG3 b, _% v. m$ C+ [9 n& F5 P" x2 c; g

+ v8 t' Q; j/ |; d5 I1 ?typedef void *va_list;8 G9 }9 c6 z" E0 i, {
! x" @# v  r& A, I, p
#define va_start(ap, parmN) (ap = ...)/ S) F* `, k% C
#define va_arg(ap, type) (*((type *)(ap))++)
% `1 A" v8 ], _+ a; J, C#define va_end(ap)( a2 x- G& l, P' \
#define _va_ptr   (...)- ?' K9 t0 p- T; c; ^8 M4 ?
#endif
2 U  d& o5 @6 r* h( N1 L( K$ S8 x# Q9 R. i
以上为"STDARG.H"的内容.
7 w$ }2 P$ F( R) R& F* O该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;/ e$ r: f9 K# E3 J# @9 }' I
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
, M# {1 U1 o% s9 l/ \! oparmN为可变参数的前面一个固定参数.7 V9 U. Q' q( ?, [+ i; R
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.& H; `0 \0 w( W* {, _( x# {: U
va_end(ap) 结束可变参数获取.
7 P) X- J- z0 W5 a3 r9 y. T. p  F1 G+ [' n  h6 Y- l* i6 v
3)可变参数的使用实例
4 q' P. X2 ^( ]* [1 R- [. P3 t
4 p; ^/ j+ {& U4 \# |$ v实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.* {8 P8 Z* S4 q5 ^
6 Y! E. E+ N( D8 x7 n  |# ]
#include<stdio.h>6 i7 F/ X( _5 V" z8 f
#include<conio.h>
- I7 l! @. [& ^; s#include<stdarg.h>% {" b! ?- H; c* U6 X2 F5 \
void tVarArg(int num,...);/*num为可变参数的个数*/
) J0 l9 x/ U7 D: [int main(void)
& a3 i% K( n- n  I8 t{6 f! t' k' l+ k+ f
clrscr();* K7 ]. [# g+ a8 v' Y; f
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
7 I- V& I7 _& t. ]' }* }5 h+ L% TtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");$ R1 O* k8 Y+ C) R
getch();
8 b8 S5 q$ A1 o) ureturn 0;
$ H# b" i, S$ ~7 X! m0 c0 l& G}' u* q) w/ Q; }# w5 p* x
void tVarArg(int num,...)& E$ T7 K3 p. Z8 o; F2 S
{) m* B" F' d# k
va_list argp;  /*定义一个指向可变参数的变量*/* @* @  j5 q! z
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
* I* [6 J0 s& M1 H& l5 xwhile(--num>=0)6 m- S6 W" I% }5 ]" m% {+ V% I
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,3 r8 j2 d: C" U) o! K
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
5 n& Z5 k, p1 b: U% h; t$ I1 T7 F9 ova_end(argp);  /*结束可变参数获取*/! A# z# Q" `, X% L, u+ i/ ]0 V
return ;. l( l* X; z7 ?$ S+ F
}
: F/ _8 @% R" R% e2 ^* s  M2 T; {( J4 n
4)可变参数的使用需要注意的问题* Z, Z% {6 t8 p0 z8 ?' L, r0 n7 d  {. m
7 T2 q/ g2 q. M0 C
1.每个函数的可变参数至多有一个.2 W1 ?' `, o0 t  Z
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
6 M9 m1 |! g) ]* a* S- z3.可变参数的个数不确定,完全由程序约定.
: s' c7 T$ Z3 d4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.& r. n2 t& \* ]! @0 X4 w3 W
而printf()中不是实现了识别参数吗?那是因为函数 & M6 T; j6 y( {% p5 K
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg ; l7 a+ u$ m& G7 N: ]
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通   `5 _+ ?# k- O3 o
过在自己的程序里作判断来实现的.
4 @! H# E% P. p, B/ i/ K& B( p5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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