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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
# p, @5 x" Q) \- u; Y. j. s它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔., C' J7 @' X& O% d7 P
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
0 M6 l+ Q. _. u, q3 c定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有1 s/ A, o0 a- K* p D
实际的名称与之相对应.( ]/ e. }* u4 E
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
5 ^/ f7 J& t3 H, k$ _" e然而,更多地自由,同样也加大操作上的难度.: b2 W- [3 t6 s
以下就对可变参数的几个方面作一定的介绍.+ v X8 q7 E p" y+ l
0 W3 ?+ L X5 e; m& e
1)可变参数的存储形式.) s6 j* ~5 a- R4 q! g. B
: { h& Z# I( W5 m) V1 s4 F大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,: L2 i4 I3 f' j/ Z, u h! q$ H
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
5 A8 j& Y, X; j$ g在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
" t! E' n8 \, D6 c( w/ a这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存./ c; A" X5 `5 R; p j& C: E
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
. Q; ]! Q) | U9 o+ A栈区:
u! v# ^, h' H( k8 F- l+ T$ g$ d7 x$ P- e& B1 Z
|栈顶 低地址 Z% f* M x4 O R* ~# W
/ @+ b: A4 F ~+ x' T& m3 u|第一个固定参数var1
2 _: p8 W- |1 H1 ^|可变参数前的第一个固定参数var2
1 t2 g/ H' I0 B! a8 ^|可变参数的第一个参数
3 \7 f5 w2 l/ _: @8 [7 Z% H|...# V- P/ B% H' G! O9 L% b
|可变参数的最后一个参数
8 d4 X, H. {) i|函数的倒数第二个固定参数var3
' B! I# k$ h& [* s' s/ A9 p4 Q|函数的最后一个固定参数var4/ C/ C+ G. D3 i$ @4 _- ~
|...
) a! e2 ^: D7 [1 b|函数的返回地址0 d6 ]6 _+ b# D) S% [) U* {
|...
( p& z0 _$ ^0 m- e8 Y|栈底 高地址
5 N9 C6 p5 K& G8 c/ V1 }& g6 f5 O. [) W+ D B
2)使用可变参数所用到头文件和相关宏说明9 i/ _1 ~5 A) l, ]
8 ~; m) G0 Z" O/ i
在此,以TC2.0编译器为参考对象来说明.3 N8 q$ } d5 O+ J3 A
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.* ~2 w' I S, S2 W$ ?0 z# D/ v
此文件为:0 t& _/ o* j; B( a) i
/* stdarg.h/ o2 q8 [# d' x/ P: ^ B. W+ i
- i9 W7 A) V' N9 uDefinitions for ACCESSing parameters in functions that accept
) {/ u- |; u+ Y) g) _- Za variable number of arguments.% P2 [1 l9 P- n* x* I4 {- S
/ z. z8 v: R. M7 w. {8 m! vCopyright (c) Borland International 1987,1988
! m* ]9 h7 B# ]" cAll Rights Reserved.
5 ~2 a% L, e$ B+ T( X*/2 U: D' b* [1 J/ y" }1 u2 R
#if __STDC__
; w4 e" Q6 G ]" Z2 Z+ e#define _Cdecl
) T, u/ w6 \1 P/ L#else
7 F0 M* g6 s0 o& t3 b; i- \#define _Cdecl cdecl3 o2 I6 R9 V( S; \% A
#endif! n, o' U1 x& [3 P
0 Z: b6 b- [- g8 B2 j) [#if !defined(__STDARG)
# g6 |& X. [0 C% O) m9 `. v#define __STDARG5 L: \; ^) d9 v& n1 K$ a
& U% V5 W( m' c! z, a( G+ `
typedef void *va_list;
! |$ l0 I4 u1 T8 w0 _4 C& G, U$ k( Z. i
#define va_start(ap, parmN) (ap = ...)- F+ H" I& l& K c% h+ n4 ~: R
#define va_arg(ap, type) (*((type *)(ap))++)
! Y, q& n* I) j+ C1 P, ~8 Z, \#define va_end(ap)' ^! i# |; A7 n1 _- y; c
#define _va_ptr (...)
9 T0 a4 _/ f5 T! D, E9 ]#endif+ r h' X0 u' D. O" M& m& ~( c
% d1 U0 u5 n7 X" S9 u" r3 @以上为"STDARG.H"的内容.
5 ~) Z5 Y: D& J5 k- R& Z8 }& P: ^该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
5 O7 w% D f D. Q! Rva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,6 I4 x E; e8 n
parmN为可变参数的前面一个固定参数.9 K7 r% P+ ^3 v0 U9 K0 ^. a
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
1 x I9 _8 U, K/ D" O! b+ z' mva_end(ap) 结束可变参数获取.9 Y. L& t6 D% }6 K: \2 W. f, O8 T2 e
9 I5 W/ E4 W' y: y% ~/ ?: U3)可变参数的使用实例% F; r& I7 U5 E( A3 G i
8 l% H6 k4 @% j实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.% T$ C _2 {$ @. s( I) u
) P, I1 Z$ u3 U0 `8 d, \
#include<stdio.h>
9 ]0 X. r5 z, g#include<conio.h>
# L2 I9 S) l$ Q& B) Z' b#include<stdarg.h># Y) r% O5 ?# u: @* ^5 R
void tVarArg(int num,...);/*num为可变参数的个数*/ n! |* z, L. N* c
int main(void)
8 @; p3 e% \8 x{
( X8 U6 X1 K- A0 U$ y% k$ Cclrscr();- i X' N1 m: r) ], g- b" d/ z p# k. }
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");, X: _& Q. g" Z( P/ ]: o7 _
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
$ V9 y) R0 ]; V" x9 m9 A5 fgetch();
3 r$ N* v" h8 Rreturn 0;
. C3 y0 A5 J5 ^. P# B! |2 }- z x}
% x0 v: P0 r0 B, r* ?' i' ?. ^' t, Nvoid tVarArg(int num,...)8 V; N! n I8 t* N& t
{
+ F* k6 q' V1 j, }. P- }" Qva_list argp; /*定义一个指向可变参数的变量*/' z: b4 l& a H. R/ f% ~
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
9 C3 ]( J0 q2 M9 K. {4 n- _while(--num>=0); y5 Q I+ u! f! b% z3 {/ r$ I: W
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
9 K$ }, |* S/ K3 S: j2 A/ [ 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
; E5 e5 Y- [8 p7 |va_end(argp); /*结束可变参数获取*// ~" |) ]; e6 w0 T$ n
return ;+ w n& R4 r; l, {2 F3 Q; p
}
^2 H6 s# r0 R) Z' U3 d h
7 y/ q+ L3 K$ ~) Y+ r4)可变参数的使用需要注意的问题
) C( t) n2 E( i/ w3 T5 \1 ~1 Y# u, A1 K, x
1.每个函数的可变参数至多有一个.
9 a" o6 c- R' H3 g2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数. c. s8 O( I' @2 i& d
3.可变参数的个数不确定,完全由程序约定./ J: g2 Q3 a, T" O
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
0 p2 f; N3 h7 Z: l6 a% S而printf()中不是实现了识别参数吗?那是因为函数 2 z1 h+ F/ y% x7 b, f
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 1 u4 S2 Q; O$ V1 w3 H' T- l+ O% `
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
8 e0 D+ ]; L5 Y' _, @! e4 L过在自己的程序里作判断来实现的. 4 n0 u& B3 o, y" Y5 B( o5 [
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|