返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument./ A0 I& x1 V" d
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.) Z1 F1 ~$ g  E" }6 w; ^
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不6 J( E% {% G3 X. _
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
* ]7 E* B# y" p7 m; a实际的名称与之相对应.
' z. R! b% g' X4 M由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.* H  h- a6 }5 [' h# E
然而,更多地自由,同样也加大操作上的难度.  p4 b* O9 H) D9 a# X
以下就对可变参数的几个方面作一定的介绍.
" z) \; ~* H8 |3 g5 ]% [! x- ^
0 y& p' v* j! U* q$ v' l1 Z7 L; `3 c  o" o1)可变参数的存储形式.- X  J+ _0 `/ U- t2 ~+ u
" j% g# h3 h7 G# k
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,6 c) G* o3 i1 G4 c
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
1 C5 i6 |7 A% ]. ~- z在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
" L7 g  `0 L: [: `5 `9 W这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.5 ~+ E' ^- F( Y* |
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
) f+ ?" P2 c" A8 A- b. i! s栈区:5 ~$ n& |0 d; t5 Z+ q  \3 g

3 G' y* F/ A$ ]* K: J|栈顶             低地址' g, \+ p) N5 T! }
8 u8 z8 Q; n% _
|第一个固定参数var10 u, p0 |5 |5 ?+ m3 e5 m
|可变参数前的第一个固定参数var21 f  M5 n4 P( [  {) h2 F  j2 t+ ^9 B
|可变参数的第一个参数: N9 w, k( y) Z' u% p' d
|...+ u& f4 s4 d+ ~( d5 m7 X
|可变参数的最后一个参数
3 x7 W) `9 z& {0 R2 ^|函数的倒数第二个固定参数var3
/ P. @" q5 ]  T+ A, g( P, ?|函数的最后一个固定参数var4
# w* G% Z/ ^" S) @2 P7 x4 A- S" Y|...
- u$ j$ {+ d! w* Q; A8 o' @6 Z  ]+ a, D|函数的返回地址
8 d, m6 t8 E$ @. _! y|...8 A$ q# J! G% a( }4 D' ^
|栈底    高地址
7 t, q% r# r! G. C7 T8 D& H3 B( ^3 v) M6 K9 _
2)使用可变参数所用到头文件和相关宏说明
3 L/ @% {7 F( a, u) F* [5 K7 D8 ~+ `! _+ z- P
在此,以TC2.0编译器为参考对象来说明.0 B$ B4 C& n- W( S, [
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.* Z4 n9 V% q* @8 P7 B9 O' d
此文件为:
$ Y0 p' I( R  N  @/* stdarg.h
; `( D  X2 `; [
$ k# j9 w1 w+ T3 A7 }/ {Definitions for ACCESSing parameters in functions that accept4 H3 v. l$ {* M9 E1 H6 J" A
a variable number of arguments., w0 C; @8 v+ q. c: \3 P* x

- Q4 h/ V/ s6 Z  V5 VCopyright (c) Borland International 1987,1988) t. E) R( Y/ {9 T) d
All Rights Reserved.
# f, F4 m3 [. c- ^9 s*/4 v* `; I1 a' X1 }0 V- J0 g# h2 c
#if __STDC__
% d2 K/ O( q( w#define _Cdecl0 _5 [( z0 A% W% c3 E
#else
# j4 J6 Z- _) _2 M/ R#define _Cdecl cdecl1 O" N( O2 s+ E! E- z# q* o
#endif
( e+ g# @. e0 l
- r9 d5 `. h  |' n#if !defined(__STDARG)
6 \' r4 V7 g3 X#define __STDARG
  c( o; q( E  o
5 x& @! E8 j$ E  H2 x( Stypedef void *va_list;. ^+ t, @) `: z' F0 V: |% `2 X9 v1 T

! R* B) I2 j# B7 d8 C" ~  e#define va_start(ap, parmN) (ap = ...)1 H4 ~/ r+ V9 Q4 e) u% _' @! U# Q
#define va_arg(ap, type) (*((type *)(ap))++): K' x- g  F/ _4 |. @
#define va_end(ap)1 |5 x; J$ ?* f# ?( E
#define _va_ptr   (...)8 P: O  ~# e) ~0 w( z; X4 U
#endif: F! t. L- O/ M4 B# j3 r4 j
, q7 i; T8 d# v9 \  j( b# u8 T8 l
以上为"STDARG.H"的内容.
4 G% Q6 V5 Q8 o! K2 P* ?该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;5 w/ l" c/ [6 t! ?9 A) r5 X" P
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
8 D9 ]' V% Y, d9 iparmN为可变参数的前面一个固定参数.
6 D+ I3 s, q+ ^' a- e5 tva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
: e4 r1 d- a2 ?8 t4 j2 A2 R$ W1 bva_end(ap) 结束可变参数获取.
& w3 N8 E" j" q6 [- H  x$ Z: e8 T- Z* A  A! r
3)可变参数的使用实例
* Z; i5 [. B9 P; j* f, U$ ?0 ]7 J& P2 z  J
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
* G, _( z7 x/ L3 D" N. [- n# [$ e4 Y) Q! @6 N
#include<stdio.h>" O( l; d9 h) _: n: n% h
#include<conio.h>
: g5 q1 \; c1 K6 G2 m+ w#include<stdarg.h>
  ~. [  V, Z( Uvoid tVarArg(int num,...);/*num为可变参数的个数*/  M, |; c5 {0 p9 q+ M
int main(void)
7 I2 E* t9 N! R% [3 f- D& j, T9 h8 n{
+ R0 l( Q, `! w; k- Aclrscr();
, T: U$ O5 ?+ B  D) Q$ ItVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");( f2 E; Z' I* f$ y
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");# \5 m$ W! y; n1 u  {3 t% X
getch();: P/ i% s6 F1 [2 p2 r0 S+ H  A( n
return 0;7 y! ^* C% D" x# _4 x
}6 I# ]5 ?- o, e: Y! ~
void tVarArg(int num,...)
0 m! I& q/ k2 l7 M! ^{
2 E4 T: K9 P6 G% y8 [* I$ c# Pva_list argp;  /*定义一个指向可变参数的变量*/
! j  D+ z: t$ X- ava_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/1 C% F* h( c1 q8 U& b
while(--num>=0)$ p  f) A4 s& R* |4 A
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,: ?: j( x) G. ~& k- ^3 x
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
! ^! t3 \3 N- y. ]va_end(argp);  /*结束可变参数获取*/1 ~. u" ?- m7 J/ ~- Y/ }5 \  j
return ;& J8 m! \& p4 l: H; {
}
6 O- ]: h/ U  X( q; Z5 ~: b( `$ m6 w- o9 W7 D
4)可变参数的使用需要注意的问题
6 c* R" D- C8 E) a# x+ E5 Y! w* Y3 |
1.每个函数的可变参数至多有一个.
: Y, h3 g2 A8 c, e+ h2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.! L# A5 C$ I( w
3.可变参数的个数不确定,完全由程序约定.
3 F% @" j" M0 \. ~6 B: m4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.1 ~) z' {" v& R* l. B! t$ |
而printf()中不是实现了识别参数吗?那是因为函数
# J! ^! y( a, q* O: w) W' S; f; c" eprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
3 i8 \4 G8 g0 s: H3 ^) ]的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
$ Z' L6 `; J3 w; J4 F1 o  ^4 s过在自己的程序里作判断来实现的. ! D" J) @0 N, m4 ]
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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