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

函数的可变参数详谈

可变参数的英文表示为:variable argument.2 N. Z! B2 f, t( }$ @# [
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.  J' a4 I8 E  H' y" ?  x
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不) F5 a, J! J# _* m
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有1 J! ]5 U8 i  U9 |/ T$ L
实际的名称与之相对应.
0 c# E2 f$ Z+ ?8 s" e0 l$ Y由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
. b* T  }& T  j8 d. M然而,更多地自由,同样也加大操作上的难度.
1 B" K0 }. u; Y& Y  g, A1 y以下就对可变参数的几个方面作一定的介绍.
! E2 z# s% s9 ?7 H' \+ x
1 R2 j8 w/ A; D: E( P& K1)可变参数的存储形式.3 b3 I7 G! ~! W' W( ~% [+ [2 S: G/ _

1 \7 y! F" d( A3 x/ q大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,1 s# H% t: N1 h. `
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
2 {6 Z  K/ z: B在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,; O7 s  S+ w& S' H$ L
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.: f* ?. P+ K* e. o
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
2 q% u: g: p+ j: K' m  {栈区:3 P% i" N. z* W2 [$ }
7 @  Q. N+ D+ G' p7 o0 ~( z& P. m
|栈顶             低地址
/ i! H# Q. R  f0 j. Y) z! K- R9 e
|第一个固定参数var1
6 N2 O/ l% i5 e( Q6 [- O8 T2 U1 L|可变参数前的第一个固定参数var2! c3 B$ ^8 e5 m1 p! O* v
|可变参数的第一个参数
3 I% y5 r% {7 U% W7 U9 \$ b/ x|...( a$ K. \5 s, T$ r9 m0 k$ L/ J
|可变参数的最后一个参数
, U+ u5 F* s# ~2 E( x; F& B|函数的倒数第二个固定参数var3
4 w- u1 j7 T+ T8 \" X4 E8 S|函数的最后一个固定参数var4
3 j+ T6 o4 ?4 e5 c8 h7 q' j|...! N9 d- b* n* B$ n6 R
|函数的返回地址" q7 @5 C* [2 F- W
|...9 ~9 u* l& O8 e. o( P: k; J6 G
|栈底    高地址
: E$ w5 t) q- U+ a$ o: V1 _3 s, J# ~% R
2)使用可变参数所用到头文件和相关宏说明9 a, ]' T* E( T8 X8 q
8 F( r- M! C3 m) w# o
在此,以TC2.0编译器为参考对象来说明.
9 ^1 ?! C2 q7 D- v+ S8 n% Q. P5 y% z, R- B可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.8 }. ]! K$ o' p3 z& |) o0 A# o! P
此文件为:
; G( Q, U. \6 }) z$ q0 m, R/* stdarg.h
- B; e8 R% C% ?# X: e+ f
6 x+ S% A' T8 b, \( d+ T+ a+ @7 eDefinitions for ACCESSing parameters in functions that accept8 ^6 f" y" O" L% r5 J
a variable number of arguments.5 c8 v" _; [0 n. v7 t' r

8 R: Y  s: ?% J: N; o7 nCopyright (c) Borland International 1987,1988
# _( y5 M; ^% U& IAll Rights Reserved.
3 [' Z* T; J7 W, {7 Q*/
. {9 O4 _/ b* k8 w$ S1 H#if __STDC__
% z" q3 i5 \5 F' ~$ x#define _Cdecl+ K3 {& N! E; I. n& O
#else
$ b* l8 ^$ A3 e#define _Cdecl cdecl
6 I9 ~# E& k0 T#endif" |& E  n2 m7 U# [( R
2 H2 {; J$ G+ k3 @9 J/ J9 _" Q
#if !defined(__STDARG)2 m) g# C: W. x1 F" s! c! i/ ?3 K# \
#define __STDARG
: _, Y8 e+ j0 D+ B/ T5 x; g* \' j# B5 r1 K- X3 v
typedef void *va_list;
2 Q5 Z$ t' k2 b! w- U. t
# c! C: I0 M: `9 W5 m2 X#define va_start(ap, parmN) (ap = ...)8 K  K2 ^% [% e3 x, p, a
#define va_arg(ap, type) (*((type *)(ap))++)
) r1 f2 N% H$ b" L- ]' V/ J, T#define va_end(ap)
- O* Z4 q6 a9 S9 Y#define _va_ptr   (...)2 a* N6 r+ `( P0 D; o
#endif. m2 m1 y9 Y7 l9 i% Z6 c

) z& C' ?+ K! R; I$ s- |以上为"STDARG.H"的内容." C. w0 Y+ z$ Z
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
/ r- \4 z. G/ [+ ^# Gva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,0 M8 v  L5 i9 G% j: f1 n
parmN为可变参数的前面一个固定参数.- x8 ?! A7 V  V6 @2 P. R8 s
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.  Y2 y& X( d, L* m5 m- \
va_end(ap) 结束可变参数获取.
, ~  W: b$ p0 X7 ?7 q# V
6 Q: g! D( Y. H3)可变参数的使用实例
. I* z2 q# }: V) h4 E8 K6 z* r1 t# m' ]
4 y5 V1 f/ z3 P: w实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
& _' u2 E/ x8 }  B- F
/ ~* {  H( j8 M6 G5 s% S#include<stdio.h>1 M# H, L0 }$ @4 }; P% p. h; i
#include<conio.h>! h' ?, s+ F  @9 W/ Q; E% r
#include<stdarg.h>
' F' ]: E# D; f! c4 ^void tVarArg(int num,...);/*num为可变参数的个数*/" Y9 e( n7 t- d5 ]8 [
int main(void)
& j- S2 D, c0 X' g8 F+ T{
& ^# ~" E0 |; m7 d3 z$ U. L* vclrscr();9 ], |4 I$ K% G2 a/ l6 h
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");' j# D! _4 q3 c% B5 ~8 ]
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");6 p' Z$ G, [8 s* @" ?! O
getch();
. F- S: r3 }/ }6 `4 |9 Areturn 0;
3 T# }& d! }* p& ?}
, D& |- p0 a; }  x0 jvoid tVarArg(int num,...)& g0 ]9 }8 }  ~
{8 o! f4 d! G  `+ F5 i; `
va_list argp;  /*定义一个指向可变参数的变量*// k- X) b4 a5 L4 D2 I/ C
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/1 R5 R6 l6 T/ t. r; f& i+ b
while(--num>=0)7 R4 D5 {* |9 m5 `' @" H
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,# J' t6 f2 l) a! F+ l
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/7 B7 O6 d3 M( _. w. ^
va_end(argp);  /*结束可变参数获取*/6 L+ g8 N( w$ K
return ;, p! B+ w- T. P1 S9 w/ L5 X
}$ l5 |; L/ K5 @0 }! P+ I" k
& I5 ^$ H3 Q) E; e6 ?
4)可变参数的使用需要注意的问题+ G; }4 H- r1 s1 ~: J
& ]' w3 M, L' f6 `3 @3 V9 @- o3 e
1.每个函数的可变参数至多有一个.
* X( I! O: |( L: v2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
* n' o& g3 D, L5 x1 a- _3.可变参数的个数不确定,完全由程序约定.: C1 n! o* l( Z" o1 ~
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
& n8 e+ J" |$ @8 ]! Y而printf()中不是实现了识别参数吗?那是因为函数
% @: B! H1 ~9 \printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
" }% o' F2 G- J$ @9 M2 g4 R的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
7 @5 r* W/ u4 u( D$ r1 g. A: Z9 H过在自己的程序里作判断来实现的. 9 b4 @- E$ M7 w: o! n% h% _1 b
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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