|
  
- UID
- 133
- 帖子
- 51
- 精华
- 1
- 积分
- 186
- 金币
- 55
- 威望
- 2
- 贡献
- 0

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
; C6 ]6 {* _$ L5 h; [- d它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.+ p1 }" C8 T" e* U
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
) E5 f3 |2 N+ B5 {定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有0 Q$ n3 S- _1 I X5 W+ a# K9 v
实际的名称与之相对应.
& x! a+ X [' H由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.9 y0 c! {2 p( q3 k2 k4 t4 r" `3 h
然而,更多地自由,同样也加大操作上的难度.
: A: {+ g% U! K6 E q5 O2 @; V以下就对可变参数的几个方面作一定的介绍.
# V0 ]; f' h9 @; i4 {) {+ N' V0 E' ]: l. ~3 z, t% ?0 N* `3 m* l5 U
1)可变参数的存储形式.
- Q% }, R+ \; D
. w1 Y* I) ~# e大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,5 [1 c& J* [* n) N0 e
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
: P: I Y& a) t! J& ^8 V在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
& ^- e) Y' z2 @! ]% M' }这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
: t$ a8 B G. Y+ H因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):: J8 a% \8 J! w. e* b
栈区: `( s3 c# b1 d( M# p
( `# C" H/ f/ t& K- I) a! @( Y|栈顶 低地址
* t1 [" F' o0 V5 Y# R S" s* w& D7 l% S3 \# b
|第一个固定参数var1
' m8 n0 U- e6 J) X0 ?- W8 K% s/ ?|可变参数前的第一个固定参数var29 H$ {* b. Q, a$ ^. D
|可变参数的第一个参数
0 R; i! l: u( D1 G/ ]- d" ^# A L3 F|...3 a! P# ~* Z; {9 G& v
|可变参数的最后一个参数' ~7 ]) r* K* c, z5 V! e e
|函数的倒数第二个固定参数var3
0 y0 O; d- K( p( W# a|函数的最后一个固定参数var48 V1 I9 I. u! \0 G
|...0 B( Q+ n# }( q# x
|函数的返回地址$ g( U) [; l* J8 q& u! z7 T
|...3 M+ I6 n) B# {4 Y9 y5 O) ~
|栈底 高地址4 a' V' Z2 z% B: n9 ]: T
- }* y+ H& Q, T4 D2)使用可变参数所用到头文件和相关宏说明
- [7 _- ]( \6 G( N' o: e1 X s- b. \
在此,以TC2.0编译器为参考对象来说明.$ l, I7 F8 o) m# c3 @
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.$ o& r9 W0 k" \7 p8 F9 l; @& S
此文件为:% E* j9 {- ]4 ?% M6 B
/* stdarg.h
7 C% d6 Q; S Y/ Z2 ^* E1 [; C6 K3 K6 q! f, m4 n' }
Definitions for ACCESSing parameters in functions that accept. [! ?( S2 Z/ X! A) j
a variable number of arguments." a) i7 U/ Z1 Y, g- p% j& ?
5 T5 P* J0 t% n. X4 I; a2 S2 N& b& E. sCopyright (c) Borland International 1987,19888 C2 W4 w" Q( E0 K' K ~
All Rights Reserved.
/ I! p* a0 q/ H3 n/ }*/
, P. x/ T! ^' i# R$ q4 F3 @5 y#if __STDC__
4 w5 a2 d0 ^4 \3 K+ U#define _Cdecl& Y; B! w9 Y7 T, i9 S
#else; ~1 d& D8 O6 A& N
#define _Cdecl cdecl
2 }1 b7 j1 Y- E#endif
4 h, `! i+ t2 p+ y
3 A8 F4 x8 s" ^) `, {& ~#if !defined(__STDARG) U; r$ ~0 w1 u! H3 U: S
#define __STDARG7 V6 S2 N9 o1 }: b, N
$ X8 X$ h% G# e$ D* w: P+ stypedef void *va_list;$ R* G& J& U/ ^% e
4 U' C% {" j! X0 i
#define va_start(ap, parmN) (ap = ...)
: ? I0 z7 l- J! m, p#define va_arg(ap, type) (*((type *)(ap))++)1 b# {" R) U" T2 C! \/ v* t
#define va_end(ap)
* b& I7 _& q9 G# a- _ W$ J#define _va_ptr (...), L* T9 E. P# }
#endif
$ |" R, `/ I2 j/ x- ^0 B" w7 {
2 X: n# d% m; [; I以上为"STDARG.H"的内容.
$ r, p8 ]% E6 g' Y& ?该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
6 |' O$ s: R. ~5 U% @9 o7 ]5 K0 yva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
' |0 _0 F2 o6 u5 X: ^parmN为可变参数的前面一个固定参数.9 V( j* v- v( {. e9 u+ |- H
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.% o: u H2 t. [
va_end(ap) 结束可变参数获取.% @0 H1 A1 R% e4 O
0 K: S! I! o1 I+ Q3)可变参数的使用实例/ A( a' D+ g; ~* [" |$ A- y
7 `# Q" E. c7 W+ g- S实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
* P4 S/ e, k5 `5 W2 Z5 ]) u4 k7 f% X3 b8 ]
#include<stdio.h>
+ w0 s" H) i* p {& U#include<conio.h>
0 c1 _$ z+ S9 `# D5 C8 a#include<stdarg.h>
~$ G3 ], F3 O1 w; X- X Avoid tVarArg(int num,...);/*num为可变参数的个数*/% I$ t/ M3 o0 J+ T& y# L" ?4 }
int main(void)
% a! t) _! d* `3 O8 Z6 K{. l8 s7 d" |0 ^' D; q9 _
clrscr();
6 m) y0 f% q$ W2 O% }0 b) h2 XtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
3 O# ~2 m C9 rtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");+ `- |" w: `) A( {: W2 A- n
getch();
6 P& x( _5 G- Q: R. {+ Mreturn 0;
* U1 m" v. X% H& Q& B$ X}
s- c/ N. V- ^9 I( \# jvoid tVarArg(int num,...)
, Z4 ?, ?) S3 A3 `* U7 i" m: i" L{
- v9 V3 P: [* vva_list argp; /*定义一个指向可变参数的变量*/ d" l, {; `$ `0 K
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/4 n; p/ @' K, p( ]
while(--num>=0)" }4 `( y1 N. @
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
! p4 K, _" }* N5 B4 L2 J 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/. J. j0 D W' z$ z' ~5 P7 R7 U
va_end(argp); /*结束可变参数获取*/
# W- j6 V2 L0 I- X( s- }: {7 [2 _return ;
; M$ ~$ M, X* @" }7 G$ d$ R: {4 ]0 V+ Y}- B5 j' m0 Q9 b5 e a# }
$ |. t) t k7 T4 ^- X4 B
4)可变参数的使用需要注意的问题9 B4 D) s6 B; }; {# c
; ?! L$ V2 [9 b# V5 `3 d! s, d& Z1.每个函数的可变参数至多有一个.
$ g% G0 @) H4 h# c, q/ J m8 V2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数., t: m L- U" B! w. Y
3.可变参数的个数不确定,完全由程序约定.
7 H+ J+ L' y! D2 ^/ B4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
- v# H8 p" h- b0 v; X& L3 A而printf()中不是实现了识别参数吗?那是因为函数 ) X+ I9 I( Q1 k4 w5 ~
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
7 k# ^, f j4 D/ {的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
- y5 n% h/ N$ H; t) L4 e& @过在自己的程序里作判断来实现的.
0 H: z- }2 p" b. ^5 Q4 [- `5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|