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

函数的可变参数详谈

可变参数的英文表示为:variable argument.% v" c3 R. @  Z& o
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔./ Y/ p7 H5 U( z* x% B/ r
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不! k: K7 v" F! R2 S6 n% p
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
: B( g3 i7 U3 x实际的名称与之相对应.
3 w' u" A6 `$ J3 @由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.( M, X: v- ^/ V* a* ~1 O; X% L( o
然而,更多地自由,同样也加大操作上的难度.
; ^+ ?$ U: F' m8 t  }" i( S/ z' U( k: S. E以下就对可变参数的几个方面作一定的介绍.# F% Z* ^4 b8 J% c& N

' w6 r3 L& h* h3 h8 D/ s1)可变参数的存储形式.
* j8 @4 y) e* g: X$ M& N! n5 ~- U; |9 H! T8 H9 r4 ], ?
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
  m' [( I% ^/ l9 U* d存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.9 F3 ]5 A6 ^5 Z
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,. y" }  G- e8 @! D: w0 ^# h( Z7 q
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.; S. n4 E5 h. A" ~: x
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
+ _4 |- D% {' q/ N栈区:5 z9 Y  @0 S5 D4 u
* P# ^4 O! r( O) y
|栈顶             低地址* I2 R$ G4 i! T7 A, p9 u& J

/ o& a8 ]3 J2 q' o8 w  S|第一个固定参数var1
  m" g" @& Q8 C6 |# }+ v6 @|可变参数前的第一个固定参数var2
, u+ @5 Z2 E. N7 t' y$ J|可变参数的第一个参数# G+ t% e2 Z; s* q; V, c% z
|...( t7 T# |* ?# v$ H, ]# i' L
|可变参数的最后一个参数" F# W/ R+ _5 a+ x. ?: [0 u. ~
|函数的倒数第二个固定参数var3
1 q  Q" T2 c  y4 h9 q; _1 Y|函数的最后一个固定参数var4
; R  A) L/ d: X4 i5 t- }|...
! k! b6 n9 J9 N* r0 B/ t& t" v- E* p|函数的返回地址
( b$ N5 \2 `' ^8 D, g' i; @0 O3 J. M|...* y0 B$ e. f0 C' ], n* U
|栈底    高地址
4 T* x, |  v7 e/ @1 B5 C9 c6 r; ^; s8 }
2)使用可变参数所用到头文件和相关宏说明
; w5 a0 S" T2 Z: }; F
0 M& i2 ~* a. g9 H" M在此,以TC2.0编译器为参考对象来说明.1 f$ Q  M9 H! Y2 p- z+ }! r" f
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
! x& H; T1 ~4 u此文件为:2 b) d0 r! p8 G' P. L9 Z
/* stdarg.h1 D4 U9 F4 b& D( K" _) a' }
% O5 V) A6 T( C4 X
Definitions for ACCESSing parameters in functions that accept
# Z9 d+ t. ?7 l" k3 ia variable number of arguments.0 Q2 u5 T( C4 ?

, f' ]; q* \, K" Y, ?4 h3 M* UCopyright (c) Borland International 1987,19883 k2 M, z5 n+ ]3 p& i
All Rights Reserved., h: [# j$ E8 o4 M4 }$ _7 k9 T' ^, t
*/5 M% g8 F$ j+ y
#if __STDC__: \  v' w+ h$ e  O$ s
#define _Cdecl
6 e4 z* _4 u; ]0 d5 k#else% i5 u# g  r/ D# s9 C- e7 Y* B, H% N
#define _Cdecl cdecl
. g/ X& u: k/ K" R- |( `#endif1 n; q6 c' T1 z! c& d

! ~" M; y1 x0 s7 a' t5 R#if !defined(__STDARG)
/ p4 E3 G6 c' a0 M+ J#define __STDARG
) l8 Y2 U, }; E% e# R* j- U  D. Q5 f8 h# v. l8 r
typedef void *va_list;
# _' j4 p! g$ H6 f" I. k. r) K* {& [. k: o
#define va_start(ap, parmN) (ap = ...)
0 l- X. b5 G" p" A7 j#define va_arg(ap, type) (*((type *)(ap))++)
: O' D3 r) U* n+ s5 x#define va_end(ap)
% C* x9 Q, o! Z; @( K, O; V#define _va_ptr   (...)" V( X) E/ }" k7 Q( w
#endif
% C9 s  O5 p8 G( W- D
1 C# j, [) g1 ~; h( A) A9 f以上为"STDARG.H"的内容.; t; e# X% ^9 J, h2 U8 i. N7 p
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;1 \+ L; ^& B" C) j
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,: e1 u% s+ F; M- h  o8 A
parmN为可变参数的前面一个固定参数.9 ?$ ?, r& Q* |9 l/ _  {
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
, g* G: l9 R. F5 Q3 h! ~; i, Lva_end(ap) 结束可变参数获取.# X9 L& s2 M: k3 d' N* o# Q

1 m+ r) P& D4 @' D2 m' N# ?6 t/ y3)可变参数的使用实例( X0 A1 _- {" n" _( K- N
; I( `% o+ w! [% }
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
  I: }! p* _! e3 o9 v- s: Q3 h
/ `' w& R2 l5 W. d* Q4 G$ m#include<stdio.h>" _. m6 Z* {! S0 u
#include<conio.h>( ^6 _/ f) R- A
#include<stdarg.h>4 Q( i, K  H# Z3 n/ [5 R+ y' v
void tVarArg(int num,...);/*num为可变参数的个数*/! V1 b0 `6 _7 ?( d) N4 t1 F
int main(void)9 x8 N0 y0 B# I0 W( E3 I
{
& \( J1 d% T4 g) G8 gclrscr();% v; |" l- k3 [; Z$ d6 g$ B
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
+ k/ t& s% q8 ntVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
% e# ]8 ]3 i, v' R% J% Egetch();
  ~0 B& V8 u3 T& {$ ereturn 0;
8 U) [' j. H" g: z& Q}. C8 a$ X& z- v7 s$ z7 i
void tVarArg(int num,...)* I/ i3 \' c! W- s
{
3 _1 P: Q  E' j6 Gva_list argp;  /*定义一个指向可变参数的变量*/2 ^3 U. E6 ^) T. j+ f4 b
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
4 l7 O8 m# @0 q8 N( A1 r6 p/ b3 uwhile(--num>=0)4 U" i5 c& @5 O6 K
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
% p% c( A4 M8 Y, H    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/5 Z9 E  j6 D# K& @0 z
va_end(argp);  /*结束可变参数获取*/
6 W1 k1 o4 k  ^+ M/ n4 creturn ;
2 ^/ k/ j8 d. E, s, X}
0 v: [7 T2 t' H4 [( z0 c
/ M0 G& H. k9 J1 i: ^4)可变参数的使用需要注意的问题8 W  Q* ^4 K7 r* f4 z

" [- S3 G& E) a; e/ N5 \# ~* H  k7 `+ v1.每个函数的可变参数至多有一个.
2 s4 O; I9 A( I2 K% f2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
5 K1 `9 ^5 d8 _0 G$ X  `3.可变参数的个数不确定,完全由程序约定.; g. Q4 I1 K0 C7 o. o8 x. M3 S
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
; A; Q) [2 v2 |; R而printf()中不是实现了识别参数吗?那是因为函数
3 O: B3 f) ?" y* l6 l: B& j9 J  tprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
7 i: ^( p; Z% B) [' ?的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 . {* z1 z& \5 ]! R- M8 C
过在自己的程序里作判断来实现的.
' r" j- c4 ?' |' s6 d, E7 i! Q5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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