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

函数的可变参数详谈

可变参数的英文表示为:variable argument.& H6 w# }8 d! |+ ]" Y5 S* R
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.. L0 M/ S( l+ ^, C& z# g4 u
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
' I" m/ @* c/ k% M& q* e定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
# [. \) H  k" ^( [7 ?$ S- r实际的名称与之相对应.
9 B# E2 v% {1 i/ j2 `+ i由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.! C5 K$ S4 x1 s; Z
然而,更多地自由,同样也加大操作上的难度.
  N9 d: t" t* h; f以下就对可变参数的几个方面作一定的介绍.
; c* h# I+ ^; Y# F4 p+ x9 }1 t
( {7 t; G2 k* y9 Y: m" Z5 i! _1)可变参数的存储形式.
4 i' u/ D/ B& b4 \7 R
: T4 A9 n2 w; w( W! |' }( n, z大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,# }. j) H5 N# K0 O
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
3 @* E) I% L) A- J& R" ]在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,/ P$ f; C) |* b! A0 {# O
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
9 J2 c6 o; c7 U7 z  F% m因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
4 M5 {4 c% x4 w0 k栈区:* E: ]0 x  [5 W2 |7 ~+ y, a6 x
4 ?- o3 |0 H8 T7 l! N* ?2 q" x) o  P
|栈顶             低地址0 P' f; v8 Q' Y
. h7 D9 J/ g1 m4 Y
|第一个固定参数var1
, M3 U" r3 p6 P; \: }$ A|可变参数前的第一个固定参数var23 M9 G3 r; C4 e; D: D) i
|可变参数的第一个参数# J: f4 s1 ~2 W% {
|...
1 \) J# f# p$ _; ^  r0 Q|可变参数的最后一个参数
) @* \2 c- w2 |9 g+ ?0 z. [' K3 {" v|函数的倒数第二个固定参数var3# }5 {/ Z9 s  s8 g% z2 o2 w
|函数的最后一个固定参数var4
# J% c  _3 M* k& n2 f: Q( l|.../ c- Q( }# G. ~& Z
|函数的返回地址
, o2 y% t4 j7 u1 p|...
1 b( f3 ?  H& {# r|栈底    高地址0 e- @# `7 m8 E5 H. s
$ h5 o9 r2 g  d) `8 o
2)使用可变参数所用到头文件和相关宏说明- X, h2 J7 Z8 j7 |- S) {9 ^

! ^1 f8 p/ ^- Z) Z9 Y% ^. a# U在此,以TC2.0编译器为参考对象来说明.$ x6 D4 H$ \4 ]
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
& v) X7 \' s. E  Y此文件为:. ?3 h& F! r5 a1 U# c% e! ~
/* stdarg.h" R* Q6 W2 y% U8 |

7 _( i0 o9 z/ H: D2 b  KDefinitions for ACCESSing parameters in functions that accept# k4 p1 _  c. r/ m4 f  ^
a variable number of arguments.
1 i3 l) c7 O6 f  a! r$ ?0 l, h: C. C6 X2 K: t; n5 \7 g3 q6 ]4 I7 U
Copyright (c) Borland International 1987,1988
! V$ w+ P3 v) ~  RAll Rights Reserved.2 _& x/ [; K9 K! C9 Y2 R
*/* Z( P3 R+ z- S; ~, U! X; X. W
#if __STDC__/ N8 [( [/ _1 H  L
#define _Cdecl  i- u' D( q# q9 ^* c- M  B
#else
4 o/ D5 E- m: o- F. p  y#define _Cdecl cdecl
  d+ L7 d# [- b#endif& ^! r4 O5 v, O/ d: ]

& T, W6 `- V3 \2 n% z$ K6 m3 B#if !defined(__STDARG)
2 b# l( `% V/ e  b& k#define __STDARG7 f4 t# u& R3 Y# @

. Y; z+ }7 f: g7 b1 ], e* B* P4 [typedef void *va_list;7 H) q1 j4 J( ]5 Y! q2 M# x

0 _! m/ r* D0 H$ x( q8 f. \2 }& Q9 X#define va_start(ap, parmN) (ap = ...)9 P- D1 I/ l' s; j/ S$ Q7 }
#define va_arg(ap, type) (*((type *)(ap))++)$ A& B( {/ Q; K3 r
#define va_end(ap)$ M- [/ f. C6 l/ _1 q
#define _va_ptr   (...)% V, N! M, l  A, Y) v8 b
#endif+ ^9 T# D' U; T( H) d9 i% A! y

3 k/ J. i% |' V% Q2 d0 i+ C  f1 G9 l以上为"STDARG.H"的内容.
, m) t# H- h0 J. ~) E该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
$ b5 R, d& M2 nva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
7 j6 @0 d. I; qparmN为可变参数的前面一个固定参数.
8 q1 w2 l& O" r! m2 ~va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.1 O; v9 d* U1 r- E) W, }3 k0 N! s
va_end(ap) 结束可变参数获取.+ [& r- A9 {. ^. P3 D% \5 C; d: @, m
$ t$ }" ^+ K$ Y, ?
3)可变参数的使用实例
+ N# b' [; Z: Y, d2 G3 w; v# ?+ y0 b1 ~5 \
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.' Q0 W& u) e- [9 q9 S( ^

9 F0 y; j1 {& G8 M1 D* k#include<stdio.h>
( \& u: b* ]8 a: w#include<conio.h>, d! ^6 q# J+ R: f( ]
#include<stdarg.h>0 D$ _+ W! o6 G7 {/ b. k5 n; y
void tVarArg(int num,...);/*num为可变参数的个数*/
: b% R- m% Y4 i! Q( d. ?8 K/ Z( D5 yint main(void)" [. N+ P& @8 `8 B5 ?* t4 P0 f
{/ W- ^; H+ m5 K8 a* t: \* U5 ]" {
clrscr();
! |% x" @$ r0 A/ `7 k8 _( TtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");2 M2 Y' z: Y3 @& I
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");8 H+ x* x2 H  ]6 M3 ~+ I4 F+ j* A, x
getch();/ S! J% T/ g% m. t
return 0;
! v& ~8 O( r) l. L% k; W* B" L}
# X: E' e' I, j# |, x% d) xvoid tVarArg(int num,...)
6 }7 }, h  v2 w. z' ~2 }) v{
  r& i$ @) `) P4 G( Dva_list argp;  /*定义一个指向可变参数的变量*/% e( f% z8 s: ?. [& Q+ w6 ^$ R
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
' |) D& T) J9 g9 T: p  R4 twhile(--num>=0)+ z  @) l5 Y  ^; k3 r" U
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,6 X- w" t! }0 v# j  D9 n
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/3 Q' q- G1 @. I3 W( `
va_end(argp);  /*结束可变参数获取*/2 Z! Q4 F+ \! Z  I+ M3 r. h  J
return ;5 y4 v/ g' z3 M# t
}* o. l& \3 ~9 y& r( {1 v

+ M$ y, p( [' r. t; ]4)可变参数的使用需要注意的问题6 T# |% o$ @9 i: W4 Y/ w) S4 h) |

+ Z4 [' y  B+ [# i* F1.每个函数的可变参数至多有一个.8 r5 A+ C2 i. _7 q  x& q; Z
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.6 F/ ^9 ?9 y) z
3.可变参数的个数不确定,完全由程序约定.
% G4 |+ c4 `0 a4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
6 j6 y6 r) m1 K( H: V而printf()中不是实现了识别参数吗?那是因为函数 & e7 \+ H# v: t: Y' i
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg - L* u4 l7 }- x
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 * N& d( A# t9 v% P! w
过在自己的程序里作判断来实现的. ) Z- n" t$ H( c
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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