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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
8 U7 m: @0 @. `2 K- c% `它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
  _! L$ L1 A5 O! b# ]7 {# z# M可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不0 v% U# x4 \( e) ^- w% D( Q
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有2 n1 R: W) m+ @& }, Z
实际的名称与之相对应.9 i. {" r  g3 ^" R
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.7 J2 P9 D+ b3 K0 x
然而,更多地自由,同样也加大操作上的难度.& i* W9 z6 k: i2 a. v  {& q
以下就对可变参数的几个方面作一定的介绍.
% E( U9 }5 P) C$ L
& Q( Q: y+ z0 R( }2 R9 c) _: G& f1 S8 o1)可变参数的存储形式.
! v& W5 J5 B0 F- \
& r; Z+ T) M5 Z( Q" X* U8 t大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,+ Q1 \3 a+ y8 h7 p- f* C
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
8 Z( [4 i3 `9 n# M: k/ y% R! ]在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,) L5 p2 _" B9 t' L
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存." M3 \9 B+ u4 l; O! `0 n2 x$ P
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
/ A$ ?" b1 v7 R& z3 e栈区:
/ t- h: r  {# W) |2 g6 F7 j/ }
9 y2 w! @! n- {8 }! z2 d2 \|栈顶             低地址; j' t! H1 N) x

6 G% H* x" U! \8 X7 R|第一个固定参数var1
. z0 d1 R  q: y& e|可变参数前的第一个固定参数var2
0 `# C7 S/ A  w! \0 O8 _+ b|可变参数的第一个参数
+ ]9 ]" x2 }/ C0 B|...3 s# H& R: S: [# x( T, O# ?/ B% ?
|可变参数的最后一个参数
3 j7 Q8 Y: v; T8 r6 d|函数的倒数第二个固定参数var3% [1 `9 s9 n0 x& E* r
|函数的最后一个固定参数var4
$ I& S0 T; K# x& Y|...0 t+ ?; c0 x$ H+ l
|函数的返回地址9 [. f3 l3 d1 e, C
|...
3 D+ X" b( t) u1 `|栈底    高地址) ^/ `$ f7 P+ w. T! C: S
; b2 F, a+ |" S6 z2 G& Y/ ?
2)使用可变参数所用到头文件和相关宏说明
% o" K; D3 j! F2 V) s
% \/ u: H3 o9 N# M. D在此,以TC2.0编译器为参考对象来说明.
5 |5 c4 {6 E. z& @0 S. R8 L可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
- u7 B) E* Q/ u: u! D9 O* w6 O. i此文件为:& r$ ?9 [3 w2 N3 C
/* stdarg.h
5 \( K( Y$ `7 h' Y6 T8 [# z. B
2 u+ h/ ~7 k' F+ @" QDefinitions for ACCESSing parameters in functions that accept
$ I# y& R  H' j8 ^- _( Da variable number of arguments.+ t" }& `, g+ {

0 I% T* c: ~8 n( Z" q) cCopyright (c) Borland International 1987,19888 B3 d7 Q- F# _- h
All Rights Reserved.
; \' X. z5 J5 g  D; _  r' z*/; P$ i; U. s8 K" E8 r  z% u
#if __STDC__/ U, s0 e) w" d4 r
#define _Cdecl
0 c0 {, q2 ~# G! N#else1 d, V2 ]0 ?. G+ j
#define _Cdecl cdecl0 }) u/ }& z! B! |
#endif
2 }- Z/ _# v! K% m; X9 c8 Z, d6 V- l; ?7 [# ~; V* e, f( X$ x& e9 ?
#if !defined(__STDARG)" y/ |3 P9 _( \) c, w
#define __STDARG
9 i: a5 W; v+ d" }& u) h+ c8 K
( {% r; L) n; O$ I6 Wtypedef void *va_list;0 ]. c- u  ~5 m$ X

/ T; w7 |3 X3 h" ~#define va_start(ap, parmN) (ap = ...). k! E3 r, U* f0 Z% b+ u$ ]# N
#define va_arg(ap, type) (*((type *)(ap))++)% B+ H/ E1 k+ }3 x) K  v- `
#define va_end(ap)
; H6 ]6 ]9 M0 l1 W' e) X% I6 I#define _va_ptr   (...)
% R! R# M# h: L+ w#endif1 _: i( q' J/ p
0 B1 A; r5 L0 Z' R8 @9 L
以上为"STDARG.H"的内容.
! h3 G+ Q) t& F该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;0 E, a1 q5 f/ n+ Z. M5 j& D+ G
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
. a2 D" X* t1 J7 ~. RparmN为可变参数的前面一个固定参数.
+ ?; E9 r9 ^$ d% o' wva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型./ h( W" T( B$ L
va_end(ap) 结束可变参数获取.; Y, n0 U5 l* z6 `0 _& o
4 l4 K1 M2 J1 B. b6 K$ s6 a- M
3)可变参数的使用实例& x* ^0 D9 R5 Z1 ]% B* _, l% |9 O; L

% w1 |4 @( J! W& m$ F& C实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.: j4 G1 v) M0 k" g

4 r4 ]- v' W2 a: M2 \4 P#include<stdio.h>
  e# H7 e4 v8 ~( }7 z#include<conio.h>6 u& ?1 X8 O- R# C; S6 \8 a
#include<stdarg.h>. C  f% W5 J3 I/ }( c
void tVarArg(int num,...);/*num为可变参数的个数*/
7 N1 h  P( T% J& A& ~8 cint main(void)' r$ w3 S5 v+ ^. j0 s. F
{* H* W! n& I/ H- d- ?" ~+ G
clrscr();& s2 y9 r* W  f/ V( l# A
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
3 Y, ^9 r6 _+ p0 i' _& @% XtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
. T4 Z# l, B1 j& P+ e; L* T! F7 Xgetch();
  j+ t% q6 I  `( X) I2 ~return 0;
: @5 H7 O' Y% F& q7 L/ V* c}
1 Y2 E3 i& f3 f& Evoid tVarArg(int num,...)! i3 }7 F. L1 V( r9 N) |
{! @/ L+ @8 f7 T  R" g
va_list argp;  /*定义一个指向可变参数的变量*/
- x  @0 e5 s# ^' Q7 G& Jva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
8 s3 ?! d1 }4 K2 z; g' ?; Q: [8 ywhile(--num>=0)
5 j0 ~& S: F" |  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
: M4 }! u; ]4 }( P/ \. T    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
- D! L! e, `4 c8 y7 x- ~& ^5 sva_end(argp);  /*结束可变参数获取*/
2 w1 z8 p- ^9 e- s/ b8 ?& K1 \return ;; c- Y% [3 J0 W$ o
}
. x% A. w# m8 q' \! n; {4 c/ _1 @# e# f& U% p5 m
4)可变参数的使用需要注意的问题# W, h! _2 q- q
* o# {, D( Z7 g2 e( D* V
1.每个函数的可变参数至多有一个.
7 O- C# f; b& Z% s7 @2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.  `3 [$ b5 A  E6 q( F: R
3.可变参数的个数不确定,完全由程序约定.4 `' x* [% B) g7 T3 ~  E) ~
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
, f# q1 Q% X% b, Q; ?$ A4 K而printf()中不是实现了识别参数吗?那是因为函数
7 C% \1 H4 A! I/ K9 Bprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
; s3 v3 D2 a8 ~$ q1 A的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 0 y9 O4 Y$ P( Z( i
过在自己的程序里作判断来实现的. . D: z3 k( X# L' o! R; ^0 |
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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