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

函数的可变参数详谈

可变参数的英文表示为:variable argument.; c6 m8 P' J1 ?6 Q
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
, y( B; I) m/ \* r, x# l) b可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不4 h3 Y  o2 Q9 B% Y) S2 E, Z1 S
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
# T+ n' r1 W# G+ o  n( U4 f实际的名称与之相对应.
2 F) Q% |. k) q7 _' l' I由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间./ ^. Q( M& X5 Y1 ~2 l5 c
然而,更多地自由,同样也加大操作上的难度.8 S3 o! ~7 D) I) g# x; p
以下就对可变参数的几个方面作一定的介绍.
6 R  W- U1 L# x  S& W, o. U+ I
0 x4 W, O& g& A' ~& g% S8 w/ w2 U" s1)可变参数的存储形式.
, E4 g' p3 R4 N& t* I% U5 S4 }4 \! n5 h
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,( V$ s" p  J* i, U" V
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
. c9 G" f9 G7 A/ r. F- N3 ^在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,% T  e2 n- k  f( o7 K
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.( R/ p& b: g  e. J3 `/ B8 i
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
3 E+ Q" N/ e3 o- r5 o栈区:
- b# w' R& e; b% i6 z& B
6 u; `; s' k$ V$ W' E|栈顶             低地址
" N4 d: Z* \  B4 X
: C+ f; `$ U4 n8 j% @6 U/ s. R|第一个固定参数var1
5 E; r+ e; q) X5 {9 i2 c) j|可变参数前的第一个固定参数var2
, Y; |/ h  U- `# G6 t/ S|可变参数的第一个参数* K# @) R+ U  n
|...$ m% }' r; t, j
|可变参数的最后一个参数
+ t* _. I8 i2 W8 ^. y* G|函数的倒数第二个固定参数var3
* d3 o& e0 g7 \5 u# \|函数的最后一个固定参数var45 Y% b$ B5 [1 a) k. G
|.... }9 z* y. F9 f9 M7 ~0 `
|函数的返回地址/ C1 E2 C$ e( V% p. b4 Z
|...: ~' H$ X4 c' V# }/ x  {: T
|栈底    高地址
/ ?  y4 Y6 l# S9 h% w4 q
; _. c* @; T  K( i  {  M2)使用可变参数所用到头文件和相关宏说明
1 ^* h4 f' R2 G3 C* j5 v- @8 R8 A+ H$ H* u; U8 F/ F: y- ?, S0 E
在此,以TC2.0编译器为参考对象来说明.
5 m, z2 t# n2 D可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.7 Y1 l% x/ P2 ^- j
此文件为:9 k( w" K8 G3 [: E
/* stdarg.h! A& `9 h: h: Z

4 u0 k& ^) P/ @- E3 G2 B+ wDefinitions for ACCESSing parameters in functions that accept
* i( Y3 B$ ^. y+ P0 O) k, P+ la variable number of arguments.2 K: T* a: U& F7 c$ B2 q, J; d/ N
; u- U, h! \% ~0 f1 g5 s4 z
Copyright (c) Borland International 1987,19887 u8 W, H9 q; S$ m  C9 Z" s$ P
All Rights Reserved.0 H- a- Z! f0 e, O  r9 z+ N
*/- ~( e' o8 U9 U3 w3 I
#if __STDC__$ ^+ b; n1 P& Y+ ?/ z( Z8 A6 g
#define _Cdecl5 H( C% }. U$ H9 |2 p
#else# B8 d* q: w$ L% y+ N# W* O7 ~
#define _Cdecl cdecl" h) S/ U1 ^" M/ h0 U! C# i, i
#endif8 [8 k7 ^$ f7 \/ z; K

" o8 _5 h+ E0 P3 p1 B#if !defined(__STDARG)
; j/ Z# p! }5 K% R#define __STDARG
' h& H3 h9 ?+ B# q, y7 z; Y+ c4 B
2 J/ D% ]+ B8 f! A2 \8 O* ~typedef void *va_list;2 o5 J2 O  o6 y3 V$ A
4 p% x4 P! J9 H% p) E; q  G" s; E" l
#define va_start(ap, parmN) (ap = ...)% y# }" |* a- i# k; c: _
#define va_arg(ap, type) (*((type *)(ap))++)
( }2 f, x" Y9 o! c#define va_end(ap); C1 ~8 w# \8 _( T' s
#define _va_ptr   (...)  R5 o4 o; G- f) ~3 G
#endif& q0 a& O% m5 _0 }% {" q1 F: C
5 G: u/ M- \2 u8 I
以上为"STDARG.H"的内容.
3 d/ u9 c- P$ ^8 C% l; i3 H该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
$ D! n9 P. R# E+ r3 h" S6 Bva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
& j, h+ d8 S, V% ~parmN为可变参数的前面一个固定参数.5 M8 x* v1 q8 ~$ ^: P
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.# f' S  [, S7 E* O3 |9 \* S
va_end(ap) 结束可变参数获取.( \9 A* O% c/ A0 i
9 r+ i. v4 q4 @0 J- _# u
3)可变参数的使用实例4 n" }; l- f0 K: I
: b0 N9 j9 J* V' [6 G" k3 \
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串." j2 y+ ~7 ]! r8 o/ a6 o* V
4 h! K2 c3 ]% w0 N5 x, q) f' T9 H
#include<stdio.h>8 C4 e$ I4 V( x/ K0 @
#include<conio.h>$ X; y4 T( F- m# B8 ]: R7 M: T
#include<stdarg.h>  G3 E$ ^5 N: D  K" R
void tVarArg(int num,...);/*num为可变参数的个数*/
. V" s  g( Q: q6 ?' U- uint main(void)9 X& Y' s6 E- I# Z5 l! ?
{# K+ S) I/ e& g' t7 j( D
clrscr();* u. G% A1 `6 v/ M2 m
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");& C" S: @- f. Z3 o  K; C5 h
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
+ b# H0 a5 o3 `7 Y* p( v/ u( @' W6 ?getch();/ d; R7 U  z8 X9 m7 \! o+ C7 m( |
return 0;6 j4 u( A, h7 `% |" b. W8 s2 ~# r
}* m: K$ \$ _2 Z; }0 K& Y3 l+ L/ U
void tVarArg(int num,...)
/ Q) d' R! N# b- ^$ F{* l1 E' f/ j" r0 Z4 H4 V$ P3 U
va_list argp;  /*定义一个指向可变参数的变量*/
: S0 M! p6 [$ s- }$ f, hva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
. A( l1 B7 I$ A4 Bwhile(--num>=0)3 O9 W" A' m) r# G
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,; s% M7 \" h" m
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/4 t1 U. r) i7 t1 H9 U$ g3 L5 o  T
va_end(argp);  /*结束可变参数获取*/
  [1 Z) [/ f5 [return ;
) Q  j" j7 A2 D) N6 B4 U1 c2 ~}
6 L) m5 |0 a( u0 [0 g' _6 r
( g- F( c( i9 v* l4)可变参数的使用需要注意的问题
& M8 c5 d+ K( Z2 i+ s2 O$ H  U9 p
1.每个函数的可变参数至多有一个.
4 S7 q/ T& z7 n8 ?7 T2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.  h% z, A4 Z) h5 Z  X
3.可变参数的个数不确定,完全由程序约定.# W! M+ Y: N1 ]9 E$ M3 g  @) @
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.! I( p# ^2 B) y2 F1 E! b
而printf()中不是实现了识别参数吗?那是因为函数 ) @" I" c, _6 |( T& V. U+ f" C, j
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg " U4 G- ~  V( }+ f
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 & E. e2 F- \% I8 ~1 W2 D
过在自己的程序里作判断来实现的. * r$ j5 L. d8 H! \! ~
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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