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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
# y& k+ R% x4 {0 r) D7 X$ n它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.  a- R/ B- Y3 u$ `9 h+ t+ N
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不/ a8 ~; L( K  J& b- w. r
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有) L. B- F5 H& ?' m4 v9 r% B  k
实际的名称与之相对应.
! {( E2 {- }$ l' g$ D由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.- @1 a4 c5 _1 w! J' Z  g' L
然而,更多地自由,同样也加大操作上的难度.
6 {+ Z$ z& \: ~0 Q以下就对可变参数的几个方面作一定的介绍.7 O& J3 Q7 G6 }- s; P% Y" [

. ?  v% K/ K4 v8 a1 c( |& a1)可变参数的存储形式.! u  P, A: f- k

$ b1 U* B4 t' Y$ |5 ^大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,6 p" [" Q. E) c: w7 R! [
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.( V  d& o3 b: ]
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,* E% E3 S/ o/ f# Y+ V  M+ `1 y
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.# M4 ?9 z5 m" [2 T  a
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
$ j  C$ v& ?2 u' j栈区:
/ H& B. M2 y+ m, X; @+ d
2 l- b! N4 `( m1 f8 l|栈顶             低地址
4 W" d  D" z( d& Q+ p. h1 N) H
( V! n) j  S- r|第一个固定参数var1/ X6 M4 B/ Z. f6 X* ?! ~: t( q" h
|可变参数前的第一个固定参数var2# X4 w: H  ?. d' t
|可变参数的第一个参数
* p* R% \7 q* p/ C7 p|...* d( i# O4 x/ \, }8 V9 X
|可变参数的最后一个参数
! e; D; V. s7 c% e" @9 r7 H|函数的倒数第二个固定参数var3: X3 e6 m- g# P( Q& S. |. I
|函数的最后一个固定参数var43 R8 C* n% A( H: c0 T
|...2 f  p6 J6 E  H. {, ]( U/ l3 u
|函数的返回地址
6 o) g" Q; y& p( V2 e|...( {% a8 Q6 e# O) e" @* I
|栈底    高地址" s0 V3 h& d" P: d  ~  ~

3 D" e5 }/ L/ ?$ H$ Q/ a2)使用可变参数所用到头文件和相关宏说明  c6 c# j3 K0 a# }
$ A9 w1 H0 ]3 b% O" t
在此,以TC2.0编译器为参考对象来说明.& P* c6 ]: Y- j
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.0 W' r) y' ^! b" m0 I
此文件为:
; v  D4 ]; t' ~+ ]9 y/* stdarg.h
% n/ T. w* `8 y( s  F* }+ w5 [3 [6 \* }. v
Definitions for ACCESSing parameters in functions that accept7 h7 n3 _9 ]0 A/ c! L. @
a variable number of arguments.
7 @# c3 J$ k6 r) Y4 }2 T
* P3 d* ~' Y4 V& iCopyright (c) Borland International 1987,1988
$ l% |( {1 `- N5 TAll Rights Reserved.
$ P$ V- L. N2 P; L*/; r/ y/ l' ]# s2 t6 p
#if __STDC__
4 H7 _# B" V+ x0 s#define _Cdecl
- _; ~  o3 @: T8 Q#else
/ Q; `* n( P- P5 C3 b* r- C$ O#define _Cdecl cdecl
; A' o* }6 ~* N3 A: @# q: C#endif
4 d+ V. C& E2 J$ j7 B4 o7 m+ j* S& Z7 ^5 }) h
#if !defined(__STDARG)
  n9 G& B$ h/ ^# @- y* u; L7 F#define __STDARG1 [! y7 J5 O9 d# L# T  c$ x7 Y0 Z6 W

& u8 b3 W% C" n- A! b& u, f( ptypedef void *va_list;
, M9 C6 \/ P; g- k0 ?2 j. x) {
4 [: V+ {' M" h9 E3 z#define va_start(ap, parmN) (ap = ...)
5 w& E% J  R; a#define va_arg(ap, type) (*((type *)(ap))++)% w+ U* O2 `1 g5 m$ N( O/ ]
#define va_end(ap)" s; S/ r+ K1 H% W
#define _va_ptr   (...)" N  O9 t# z% T- ?7 I
#endif
4 a5 }1 s! i+ ^' W. i; i3 G# l: i% e: {: w* R' b, ?
以上为"STDARG.H"的内容./ D2 E; A) p8 K. C, `
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
% C. ~9 u6 c7 P) `8 iva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
' e* G! Y6 S8 e3 I% tparmN为可变参数的前面一个固定参数.
: A* F1 v& C# w% w4 u5 Tva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
9 w' M, i/ j6 {) S% |; V/ |: e) Jva_end(ap) 结束可变参数获取.
( y& Q) z, c( ?0 m( E! C0 g) G! s) H2 N
3)可变参数的使用实例- A* R% w4 F4 ?, I# R) L
$ x7 r$ U& k# A2 H* P4 O: j
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
4 j+ Q( O3 E8 S+ w6 R
4 x! }  J5 L& @+ E% R) f#include<stdio.h>
0 Z0 |7 F8 @5 T7 a, f8 s8 H) u#include<conio.h>
9 }9 l1 R  f$ h! ?. f; n' s#include<stdarg.h>1 |$ W) E- U# B0 W7 X
void tVarArg(int num,...);/*num为可变参数的个数*/. p/ j2 O4 O; O) x+ C
int main(void)& \3 g3 A, O& p  m5 r6 ^( B4 e
{/ l+ ^% K# E- }9 M- u! O  l1 U
clrscr();
9 f; X5 [, u* J1 {8 StVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");' q, b+ B1 S$ B; r2 h0 B
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
- U1 z$ p- l4 u! o- u4 Agetch();
4 k; l- E) }% S9 Hreturn 0;& Q/ F. {+ n0 g
}
1 \* y1 G) x7 I+ }" Dvoid tVarArg(int num,...)5 F+ }8 A4 t4 Y0 l% G6 j& ~4 H
{9 o  d' i3 l- }3 j9 a+ }& R
va_list argp;  /*定义一个指向可变参数的变量*/
1 C, P- F  `7 \  a+ v2 ~" J, [" hva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/$ q  [# k8 P& ~% e/ g& D
while(--num>=0)
* i" H) r# R/ ]  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
' B% m8 Q% j7 q. Q6 V2 {    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
/ j, l1 M8 t, \7 W" [+ Tva_end(argp);  /*结束可变参数获取*/
; T$ t1 S) U$ J* W* Q$ W  t# Sreturn ;2 \5 i1 {( U% W: a7 P2 Z
}
- ?: {; f/ }  N! F! S, S6 O: K! }% A% n  |" U
4)可变参数的使用需要注意的问题
, p* {$ }' g0 J9 c# d5 K, q# I9 x
. ?9 N: ?% W  Y& j1.每个函数的可变参数至多有一个.6 N  v- a6 Q- P( }5 K! }
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.  u) I) K" L# m/ [% m4 ?
3.可变参数的个数不确定,完全由程序约定.
8 w$ t! J6 a! r  M8 y1 W4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.' w1 Y$ L' C/ g- _4 @
而printf()中不是实现了识别参数吗?那是因为函数
$ Y; J" }, `& g, m5 h0 Sprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
2 |8 }' F6 N7 l1 _# L# W" q7 Z+ i+ T的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 : l* K! t8 p5 q# M2 |& U$ u
过在自己的程序里作判断来实现的.
$ x# C$ W1 |3 U5 @8 H: r- b5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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