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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.: B( h' {3 u$ x5 f6 X6 k6 Z
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.1 N) A/ A& X! [: S. ^. `0 |
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
. `: p! E% z6 l4 B定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
8 U' K1 X5 p( G9 H1 y实际的名称与之相对应.
3 {; Y$ }* t! q& C由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.5 n0 ^6 p- n7 g! A2 q
然而,更多地自由,同样也加大操作上的难度.
" m$ K; _ E- k以下就对可变参数的几个方面作一定的介绍.3 {* p- T( |! t1 q+ p# G+ u7 q3 {
d( Y8 M) A6 _0 ^& l$ _; ^1)可变参数的存储形式.; F I4 U& f- H T1 q- t8 i
- T' ~& p9 Z& }大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
' V! Z: M5 e; R, N( }存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.- V2 y; T% }+ D2 G- [2 o
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
3 Q6 A" _5 Y, k4 T. h' R8 @这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
3 c( G7 f- }/ R% W因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):/ \$ k4 ]: E0 r2 q
栈区:; k0 Q$ ]% S, _* I3 Z
- r# `8 F& w, h% O! H0 B' V
|栈顶 低地址( z) b) a( L0 A: P- n2 B
* Y2 S8 _% ?9 _: B) E3 Q% ^|第一个固定参数var1
, y0 }& {3 w' k" n4 y|可变参数前的第一个固定参数var2' C; c) w0 x1 `2 E2 y, {
|可变参数的第一个参数/ h8 @% N! y; O1 `5 P
|...
0 G6 x) Y, x6 ~2 S" b|可变参数的最后一个参数
" K4 h6 }' h7 G|函数的倒数第二个固定参数var3
/ f( u2 i9 g# ?2 Y/ D/ X6 B|函数的最后一个固定参数var4
7 d7 h% u! c! ]|...
, a. o ^- u# L2 G7 }7 a|函数的返回地址& e& I* }8 A- E [ Q5 T
|.... r5 E, `0 p' l u# a+ B
|栈底 高地址
' K% l; b; v3 I, M. x9 Q' Q4 S$ e
8 U. }! E B' k3 P2)使用可变参数所用到头文件和相关宏说明$ T2 b) Y W! e1 `* n
3 V6 B0 y' i/ l- n( d, u- X
在此,以TC2.0编译器为参考对象来说明.
, h3 M2 t8 q# T' c$ h可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
) M; m8 L) C0 T9 A5 {% ~! m$ V此文件为:4 m: K9 v' Y$ s8 }
/* stdarg.h- J9 Q$ G! W7 ~3 X) W
3 ]* l/ ]. U5 g2 t" Y
Definitions for ACCESSing parameters in functions that accept% i! {- ^3 r# m) H5 C
a variable number of arguments.1 Q$ }& j0 w9 h8 X3 r2 v& U5 b: a
$ V7 _0 B4 `+ V1 JCopyright (c) Borland International 1987,1988% F3 G4 E# G7 s# V" l3 S) P6 i5 C
All Rights Reserved.
6 _& I( X- O) z3 A*/
% U2 D4 J* j; @8 j#if __STDC__
# g s. s' t: E/ @: e$ A2 k#define _Cdecl( t" j7 h& a5 D& X
#else
) S, N% H4 a! _, H- u#define _Cdecl cdecl
5 d: A) ? X. u+ e8 o#endif
# g; Q- h& Y( {+ d5 g1 s- l6 k6 {3 d3 {7 d2 ]0 L0 P5 R0 E# W5 I6 O
#if !defined(__STDARG)0 X, f& w- }& U3 [ M
#define __STDARG
$ E& O. R2 D2 ~" ^. Q
5 N k* j7 K1 F L' H9 t/ stypedef void *va_list;7 A2 Z# \0 n9 b) W# e4 p0 |. Z
; u1 j a. W- d p+ A: X#define va_start(ap, parmN) (ap = ...)
" n7 l3 n& s$ w2 ^% s: b8 Y) ^5 N/ ?#define va_arg(ap, type) (*((type *)(ap))++)
& s) I" k, c1 q: `#define va_end(ap)
$ Y( W! k6 e& B, I& {7 D0 R#define _va_ptr (...); `1 g, y: S5 ^& f$ {/ F, N8 B
#endif
& M# I) r5 X; O9 X5 K* B* n2 s0 _
以上为"STDARG.H"的内容.
+ e% ~: l* n8 Q' k该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;& |8 N. R# y+ n; y( R% B Y
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
* ], T+ @! E* a! W% Q/ SparmN为可变参数的前面一个固定参数.3 m0 {3 K+ l+ f9 D7 {+ U
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
4 c4 Y% t2 ~: |: ~* B( iva_end(ap) 结束可变参数获取.
5 |. }" d2 V# S j3 `2 n9 l% x
$ P {, b8 y1 I9 a3)可变参数的使用实例
% a! ^' n9 M; U7 V+ x) _; Y
- l* q) u& k- x% w实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
0 C1 B b' L. L( n& L e5 [0 [% M" n4 P' |& D! O9 W- V* S
#include<stdio.h>9 t' T* M: U, Q# M$ t& l* o0 t8 Q
#include<conio.h>" B( J8 Q; ~( d, N/ Z8 V& @' }+ m' f
#include<stdarg.h>6 Y9 e; N% L) D1 l3 F
void tVarArg(int num,...);/*num为可变参数的个数*/
$ v, G7 e U, _% @5 \) y9 aint main(void)
9 K: c8 n0 z3 x+ e9 M{
; k$ Q, C& K! s: r5 q1 O1 qclrscr();
- e. U# A4 U" \) W" ^& G( ftVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
6 z E& N2 f) P$ d( ?4 M/ C0 jtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");4 \9 G+ I0 F9 J3 o" a: ^ [
getch();
% m7 {5 L t$ E& h, S# o& |return 0;0 `8 v( A+ m/ q* J" n$ L
}- M8 t9 r9 d' Y/ f E
void tVarArg(int num,...), h6 p/ z- x; l3 T# I H
{
& c; G2 @* e' e$ |) N/ O. qva_list argp; /*定义一个指向可变参数的变量*/
+ u6 E. T3 N7 Y* L$ |8 T/ Tva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/0 U% A/ r( H9 F+ A. T8 c
while(--num>=0)2 U$ ]# \6 Y5 Y- v% A
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
! Y9 x! w" h) l 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
9 [, _' G) z/ f: p- a bva_end(argp); /*结束可变参数获取*/
) K% z3 U% W- l/ S; Hreturn ;/ I& P6 k$ U' j4 X) N+ O
}
6 h) Q. n" S0 ?4 T& n; c+ p$ y+ @4 }! ?$ E
4)可变参数的使用需要注意的问题% v0 Z0 B8 l: K/ Y4 I8 P/ |
: D( K' a( Y$ U6 q) [1.每个函数的可变参数至多有一个.
( P7 q; \2 h! J! g5 B) D2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
& l. g; O: n" P8 n3.可变参数的个数不确定,完全由程序约定.
. O( I- T0 P9 Z3 T; ]4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
0 E# e v/ K9 @+ e& ]* Q5 @6 T而printf()中不是实现了识别参数吗?那是因为函数
% D0 b' E4 t9 Z# R0 N' lprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg + Q& i. {: F& R, @4 k& r
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
/ J. D4 f" b# T6 J& Y7 M G9 x过在自己的程序里作判断来实现的.
+ c- p0 G5 w7 G6 S5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|