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

函数的可变参数详谈

可变参数的英文表示为:variable argument.6 O1 I- r. @& a4 ?  R
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
. A" ?! x0 b/ l" ]+ C7 v# X* A5 V可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
3 `' n3 ]% w) d定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
) a+ Z8 w! M$ H4 s9 d实际的名称与之相对应.2 b" ?! P. i6 O
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.* I0 N' j' `: o( ~, }6 h: `
然而,更多地自由,同样也加大操作上的难度.
. T- r1 @4 o  o! E& ^6 T以下就对可变参数的几个方面作一定的介绍.
) r: G- L+ `7 f; }# a
) l: K2 U1 P4 K9 B, X1)可变参数的存储形式.9 H- ]3 l8 S& k! U3 v
$ G3 @  ?  C- V" T  j+ V
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
& V9 T8 o$ W6 J  |" b/ @存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
# u1 a+ j. t- [7 c5 U$ s1 {在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
$ b# e+ h$ a1 u& A这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
( r3 F; L  B2 C* R2 }+ ^0 V; i3 n因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):2 |3 Q. G  ?- P" y& D
栈区:; K9 w/ S. C0 |  a% e

0 o$ s9 o; T, a2 i|栈顶             低地址
/ q# C7 A2 ?$ |# M, ~; S" ^; b" @: a; p0 j$ r' }( ]- ]
|第一个固定参数var1
5 R4 h* t+ t; d! t, h( `- z- }  A|可变参数前的第一个固定参数var2
: H5 _" N% J8 G$ C# \& f/ |8 K|可变参数的第一个参数
0 n$ f) D1 ^  ?) N3 s|...$ @# U2 f% H- ]7 D
|可变参数的最后一个参数
- T6 S3 i: W$ p! p8 r2 Y|函数的倒数第二个固定参数var3+ F$ q& b% T5 ~' K8 \  \( }8 W
|函数的最后一个固定参数var4, g+ a+ I% C* [
|.... j/ l% j3 b) S5 B' o
|函数的返回地址
, d. K2 I; `1 ~9 f+ c|...
  ~/ G7 _  U' J' [, p7 Z|栈底    高地址
+ N  m$ h, W+ B6 R0 x5 D5 B, I2 j
+ o& s5 s2 B; s  `2)使用可变参数所用到头文件和相关宏说明5 b3 N, |# b. O7 F9 G4 C( T# K% M7 Z

6 W% p$ w; l2 k9 }- A在此,以TC2.0编译器为参考对象来说明.( {3 f) r9 f& T8 Z
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
' Q7 S$ A9 H5 U此文件为:* P! i7 M$ z# q! \
/* stdarg.h
8 G0 k8 y5 n$ {# U1 {  y/ W5 `
  N4 d' s( y2 ?/ }% w- a. {& b& X% rDefinitions for ACCESSing parameters in functions that accept
3 ~7 E3 @: }% g/ C4 }! |a variable number of arguments.
) \9 C. R: G* C2 i. f
2 P  w, C- x! w. H+ fCopyright (c) Borland International 1987,1988: x1 X( N; G2 I3 k2 l' @
All Rights Reserved.
( S2 c/ y  m( D: F5 w2 d*/
1 W; v1 n1 b. K4 o' l1 H( Q#if __STDC__- E3 x  _% _! D% e: y
#define _Cdecl
; M5 q5 Y0 }, R" I7 @5 J#else
7 G" p& d$ g* ^0 l& F#define _Cdecl cdecl
/ o+ H* u& ?4 D1 T# q#endif
$ m  N0 H" T( ]' r* g% B. r# k( o6 I* ]' c+ ^' G( Z5 m0 |
#if !defined(__STDARG)+ W, _" U6 e9 z
#define __STDARG
& m4 p# L1 H8 u3 v$ G% @1 o4 _' U
typedef void *va_list;
/ r' m; z" F0 a" W5 L& R! }6 c7 [$ M. |" w) b/ r& w9 I0 b# N
#define va_start(ap, parmN) (ap = ...), i) x4 p3 v4 M/ G5 H  f
#define va_arg(ap, type) (*((type *)(ap))++)" i* r( D9 E5 P- j6 }* b
#define va_end(ap)
8 E, s  l: M0 K4 W: z. P#define _va_ptr   (...)
7 q0 k# j  F0 i0 Z/ u- Q#endif# C6 `' H9 ~' x( a; j" x

3 z" Q  j9 l% f8 U& y9 B+ T, @以上为"STDARG.H"的内容." m; k8 c: j. y6 {1 P
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
  C, `+ P- [  N2 U/ Yva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
/ c) ^% X4 `9 V6 f! }; m& l$ `parmN为可变参数的前面一个固定参数.7 G) z9 ~% N1 m7 j9 t7 C4 I7 @* z! o
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
2 s) F4 u# M: D" E( B* S) c( W" fva_end(ap) 结束可变参数获取.4 G7 D# s# @- @6 V6 b2 K

9 d; [; @0 R4 n3 j3)可变参数的使用实例% X6 N2 r5 n. Y7 J' |7 x9 E6 n
  d. j+ [  |0 r. i; X& g  V6 }6 N
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
$ y7 J3 n0 n/ J% N" n6 w$ ~+ x' d8 e& g
#include<stdio.h>9 Z2 @8 ?' V! C
#include<conio.h>
7 u% K# Y2 i% z0 ^# D  C4 k#include<stdarg.h>  K. X) v6 d7 y- L
void tVarArg(int num,...);/*num为可变参数的个数*/
3 J' T- A9 P) q4 E6 V* Dint main(void)
! D( L4 r! m1 S( _0 s. K0 Y{$ c4 z. }' e# X6 ?% S' l
clrscr();
; z( C8 p4 F5 I) r& wtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");# H6 N: p* X  w: r, X1 ^$ j
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");1 t7 y4 C  h( y
getch();
6 G, L9 K0 ?" T8 U+ J# {return 0;0 h; x4 y- @2 \6 u& P
}
2 a% n. i# y& e7 L  {+ a9 l. Yvoid tVarArg(int num,...)
0 W% i2 k6 E% s' z, X{0 a- I: E; n0 V7 v% k2 m8 U' [3 d
va_list argp;  /*定义一个指向可变参数的变量*/
# I7 y: E$ C; K! gva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
5 \9 n$ i1 L4 p4 w" O& Fwhile(--num>=0)
1 h( |* x, M) z2 u* |7 b  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
$ B, z. I/ S) s% K) i$ t    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
, v/ u, Q2 x9 \va_end(argp);  /*结束可变参数获取*/
% e! d: A- E$ }+ ireturn ;
& F8 ^) J5 X3 _}0 g, q7 H$ e( s' |0 L8 n3 v
4 B& ~* x0 j& T. @0 Q
4)可变参数的使用需要注意的问题
7 f/ u3 _! Z! s# k& |$ E+ _, M
; I5 G+ K, P5 I  x/ W3 J' {1.每个函数的可变参数至多有一个.  a- [4 @7 A& }6 B6 X, N! ?: u
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
% E; q; a3 {$ T* ~! R$ u, V3.可变参数的个数不确定,完全由程序约定.% `, y! N; _7 f7 g
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
/ d# o! g* K; F$ p4 h而printf()中不是实现了识别参数吗?那是因为函数 ) h4 v, s0 A2 U
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
. [$ K" K- t1 _, {: |的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 ! p- |* Z" H4 Y$ F; z% G0 g1 D
过在自己的程序里作判断来实现的.
* H0 j( }1 b* o( `* ^: C5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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