返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.
: z* j3 I6 f! C. s它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
: ~$ I2 U- q9 ]( ^  T可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
+ H! `9 Z7 R2 `定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
* c8 `1 G" F) ]7 @) x实际的名称与之相对应.
, H  t1 U5 {7 i$ A+ c由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.0 Y& t9 o* s) }
然而,更多地自由,同样也加大操作上的难度.- x* Y) ]: E$ A. b! ?' [
以下就对可变参数的几个方面作一定的介绍.) X& x4 f! f, D' D7 F5 [  B

3 \: s( N+ l! s! u1)可变参数的存储形式.
3 @* Y* ^) r( p( V
+ z8 S8 X6 N- w* f( I( w大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
% o* `" D1 t2 Y9 n存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.: m/ O; O' l& a* _0 u: \
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
* p" C5 \2 T) O) L+ E# W$ A这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.0 @! b, M$ t) {7 i8 V. z) L2 P
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
% m6 D6 [8 k" h( W4 R栈区:
+ o) o/ [+ Q* I4 ^6 |4 y# B' P0 V+ x0 N
|栈顶             低地址9 a3 p6 X" |" X; Y% ]
$ g* n4 \# o2 M% \% q
|第一个固定参数var1; {! v0 o; H' k7 l
|可变参数前的第一个固定参数var2
! q& g) ?7 o7 W5 `0 \* L- I0 ?|可变参数的第一个参数' V* Q# a9 b& x+ i4 ^& M) a
|...6 G/ ^; m0 R, W3 @- Y" C9 h& ]
|可变参数的最后一个参数
. A9 a/ C/ ]0 o/ Z|函数的倒数第二个固定参数var3
0 Z7 H+ Y' @- _' H|函数的最后一个固定参数var4, @/ A' V7 t9 Y7 D! y( e
|...
  Y/ l0 S# p' O4 i/ U4 M|函数的返回地址
. A" M* ?8 s/ _, X6 }' c4 i1 o7 ]|...8 m* ^3 O( `. W7 h% @7 ^
|栈底    高地址, r9 z; q+ t) Z! j3 |  A0 T
( K0 a. W9 \. K
2)使用可变参数所用到头文件和相关宏说明
! M$ j" B: d' m. ?! T
4 @. N/ Q" ^% I* o0 z% c! C; n; e5 t在此,以TC2.0编译器为参考对象来说明.
# t. m$ R* R8 m! i; s; M' M可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
% Z$ C9 f9 L4 F* N% }% {此文件为:- _0 Y  ]9 \% l- i
/* stdarg.h0 f, o# C* G0 l7 p) Y0 @4 y7 p3 t
2 T% x- ]+ A2 E1 j  s( U
Definitions for ACCESSing parameters in functions that accept
8 \5 ~) y8 {9 T0 Z' ?8 w  aa variable number of arguments.
8 X0 g. F1 [/ t; G% v0 V- q4 `+ |- X% u' u# D& H  u  X7 E7 ^
Copyright (c) Borland International 1987,1988
$ D* r. \* A  i. n2 cAll Rights Reserved.
( [6 b0 l- X+ b7 |1 ]*/
; s; [9 `0 C8 v5 d) ]6 u#if __STDC__& s7 D" A1 V& b
#define _Cdecl7 o0 t: e; [, P8 Q. [# s
#else
9 y" q' H1 N% [1 s- z* ~; N; O#define _Cdecl cdecl
, C8 m1 f: ^2 I3 e#endif
) L) u+ Q  y& a7 T+ ?9 K7 ]2 f& W% R5 f; r% @* O! u
#if !defined(__STDARG)9 f- {; E& r$ E+ w, h
#define __STDARG. q, Q6 V$ I, a% {; C

: ?4 Z2 t7 v( I+ r$ ~+ y  s. j7 Ttypedef void *va_list;
* w7 \# t( {( K) h# n$ q! l
0 o% D$ T7 M, c- ~#define va_start(ap, parmN) (ap = ...)  W8 S8 m- {' Z: u4 v+ v3 s
#define va_arg(ap, type) (*((type *)(ap))++)
% \: H9 J, C! M#define va_end(ap)1 I- c0 F) q) g+ i/ Z% E( ^
#define _va_ptr   (...)
) ~) b: F" }* P$ K4 d- {$ P1 s#endif* u8 X) I* ~$ S$ A$ ?
+ h% N) N3 F$ c8 N$ @
以上为"STDARG.H"的内容.7 M; H3 Z: O! s: Y+ c3 G
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
- [0 u% ?" `: ]" B: o0 u' P/ }va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,; A7 ]( e1 ?8 l+ E9 D
parmN为可变参数的前面一个固定参数.: [; a" f  C( A  v: U. Y6 ^
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
- {0 |3 G, \5 M; w$ K6 Cva_end(ap) 结束可变参数获取.) d' r# W' D9 T) r/ n/ Y8 L
4 x/ X5 F1 Z( |: d2 Z% ?  ~4 j
3)可变参数的使用实例2 D& [9 [  R% ~! i2 u+ L

: O# x2 w$ `  L1 k) Y) {; \, Q" B实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
4 U$ b, g' j4 X
# u, H* }5 C/ [( b0 M; o8 \#include<stdio.h>
# @0 Q$ K* n% e* h3 s9 s#include<conio.h>
7 Y0 U& C3 s6 h#include<stdarg.h>
4 t9 E: t5 y' Pvoid tVarArg(int num,...);/*num为可变参数的个数*/( s% Z. K) D. F* V5 _
int main(void)
2 g& h* _1 n6 |4 g/ k{6 K) ^% L+ L/ ^
clrscr();- ]- t9 i9 z# H; D6 S
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");; c1 d) G1 y# b, j7 x- L5 N
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");6 ~4 F* [! |/ E& d8 D
getch();7 Y. N/ I' h" K+ o0 @
return 0;+ Z; J/ _7 j. G$ T! \6 ?0 V$ i1 R
}" j) \# r- \+ ?, D& A* i3 ~
void tVarArg(int num,...)
+ R: E, H2 d. a& P$ B{1 z; G% g" F( x
va_list argp;  /*定义一个指向可变参数的变量*/
( ~( x$ n" ~8 _$ l1 E  Q8 hva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
, I8 v* @: Y8 o8 O4 Z) w* E# zwhile(--num>=0)# B6 T, @( M( p8 g# h
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,% V! V, P6 A/ p2 L$ }: ?
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
/ C) E* d. U6 r; y- R+ i/ }! wva_end(argp);  /*结束可变参数获取*/
: ?# I6 B" I% Z, T# i9 greturn ;) u+ C4 e; R( C6 ~5 l9 t0 s5 H
}
! o4 w. J( T! o2 K) O
' |1 @% t, {$ W# U  a) s: x6 Z/ X4)可变参数的使用需要注意的问题
2 _1 d( A$ K' y1 K" w2 k2 R% ^
- S& M! A  j' t1.每个函数的可变参数至多有一个.5 c8 |, t" m! Q- K5 ^
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.6 B* f! a8 g5 N; ^$ h6 P+ c: ]- q
3.可变参数的个数不确定,完全由程序约定.
3 s1 ?1 g* x5 F9 r: w* J4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
5 ?6 {$ A3 q/ j% F而printf()中不是实现了识别参数吗?那是因为函数
8 Z2 m' V3 W2 ]; f+ h2 O2 rprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
& Y& o( v1 j, g" r2 s) L7 z) ~的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 2 h8 \# X+ [. ?- D
过在自己的程序里作判断来实现的.
7 q; E7 }, }; T' T8 H! u$ a5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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