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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.3 |! I# p; {* F* Q3 K1 Y
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.( _8 q: I% ?& C, K; ]
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
* L: K6 s0 f8 k3 ]) a* l0 b定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有- n6 n; A( w6 J* M3 A5 v% ]
实际的名称与之相对应.
" l' h% c% ?/ K% a3 h1 i由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.2 {' x; V$ X7 j9 E; W( t; P
然而,更多地自由,同样也加大操作上的难度.
$ L: u; M$ s1 z3 M: v4 c+ A* t以下就对可变参数的几个方面作一定的介绍.: Z3 p' ~9 n3 K' ^
: o5 O* p& P. W" ]$ B( T1)可变参数的存储形式.4 @$ r. p* J2 L3 s3 @+ U
1 i, {! u2 Q1 J2 t$ G( [& Z
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
$ n, x+ y8 L9 |存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
- h. Z2 {- s- u" s0 u" ~在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,) H. F# V, { F' n9 v1 O! g
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.- F8 T& h* A' J+ h+ V
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
$ M9 ^& I E3 j栈区:0 _4 P+ L( ^: T; p( q9 W3 U
8 `! d( D8 i7 B/ L|栈顶 低地址" k5 v1 c/ E! \+ m4 M
3 S* `: J6 l" d q* |1 |5 Y( _|第一个固定参数var1* S) R0 O8 q! Q* H: [
|可变参数前的第一个固定参数var2" j7 b+ V% Z5 i9 j1 S! O( C( O% L- Q$ j
|可变参数的第一个参数
* o/ C! w" d! n" v+ |1 X1 ]|...
j* \: P" _6 z- [" g I/ f|可变参数的最后一个参数
7 M4 K6 q! m$ g|函数的倒数第二个固定参数var3. s% e8 r' J1 y4 C9 R
|函数的最后一个固定参数var4
, |! e6 l* e. L) o# I8 w* ||...5 \6 j3 B$ P" g$ d$ T
|函数的返回地址
" y5 \. @/ G7 p, A- B|...
" a5 e. F% n8 [1 }4 j|栈底 高地址
: o0 H# [# F. p, D0 W' L8 A" g/ x0 z5 n, }7 A, J5 e
2)使用可变参数所用到头文件和相关宏说明
9 f$ b/ z: m1 g3 F# |, b( ?3 C. T% ^
在此,以TC2.0编译器为参考对象来说明./ s6 P/ Y- h; t9 {
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
( C1 R. x3 t. x+ K此文件为:# z9 ^3 q' g! }1 x
/* stdarg.h3 A7 j. Q5 _, O
9 F# Y" _0 b2 m) o; lDefinitions for ACCESSing parameters in functions that accept
! n7 ^# n; e0 U: y4 Pa variable number of arguments.
, m6 N/ S" `) B0 h! m
5 K0 T2 K$ H& G* ] p8 sCopyright (c) Borland International 1987,1988
$ b# T/ r% P8 |1 FAll Rights Reserved.
1 p8 d. s/ A. w1 g0 h9 s*/
* C) F, T( T, n% g- P9 @#if __STDC__ M4 f6 {7 j8 a4 _: f/ |/ a
#define _Cdecl
" m8 z) E2 L0 {9 h; h#else$ |5 H9 p/ L6 k0 i% A P- R7 Y% w
#define _Cdecl cdecl
2 O1 o2 a/ y1 p#endif% @; Y6 m1 T, e) M0 S, |
4 H f5 _, l- Q. k
#if !defined(__STDARG)
% [0 R* U' _0 A! K& @- M#define __STDARG
0 ?/ v$ ?% G+ @# V9 Q3 P) v/ X3 }9 V
typedef void *va_list;2 M( Y( N- I. b+ A9 K M
% @ J) p/ t6 P) n- o" f% ^#define va_start(ap, parmN) (ap = ...)/ N8 @/ C! V( n) `8 @
#define va_arg(ap, type) (*((type *)(ap))++)
: u' |6 q( ?" D- h#define va_end(ap)
% _; v& e' _ H# C0 o7 u3 O' l/ L4 A#define _va_ptr (...)3 h0 N/ U4 V# G3 b$ U; n u
#endif2 v3 m5 r" s' T9 ?! [4 z
* |! d% ]' Q" p% R5 B
以上为"STDARG.H"的内容.+ E# v1 T0 c& ^
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
( O& V/ S3 I+ E# wva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,, w6 n) X* v0 g1 Y, i
parmN为可变参数的前面一个固定参数.
) K" A- r6 L& u* U t' \. v; Hva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
/ S; [. ~3 a' o. _va_end(ap) 结束可变参数获取.
4 [, l( @. e. i% E+ S
3 c( M2 l; ~- G2 N3)可变参数的使用实例* B- @. \, B3 N
3 B9 ]6 l2 K- T8 o8 E* {+ ^& Y
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.+ n; X7 c( `2 ?5 H4 X% y' c
0 G; {4 N$ p1 ~( i0 {
#include<stdio.h>
( L# n( P C0 }#include<conio.h>
4 O) |) b3 H3 y#include<stdarg.h>2 B& i4 w# g6 T9 Y0 Q) \1 ]! O
void tVarArg(int num,...);/*num为可变参数的个数*/$ T2 M* o8 K9 k- s9 O, v
int main(void)
, s3 X+ |: X( x% | Y% R{
' d1 n) @/ c1 p, z3 Yclrscr();
, L2 B T- u7 ^- X3 j; M: l; [tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");; ]- l4 Q+ v3 U% w
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
- d/ |0 v/ d* l! |7 vgetch();/ g5 A: r( E7 i f( m. }; k. J
return 0;
Y% L5 q$ `2 p2 {6 g}
% J5 R0 _9 L$ c7 Uvoid tVarArg(int num,...)
+ e9 a* D, j* r" x8 \ G) S. R{
- ~8 w$ F; s# Q+ Y9 ?: zva_list argp; /*定义一个指向可变参数的变量*/! ^, P# }. k$ k+ t7 U1 z. s5 B, \: p% E
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/; |$ g Q2 c5 `3 v
while(--num>=0)
9 S$ A+ U; X+ c: l; z& ~9 O; u printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
5 k9 ?/ i, G: o$ X( t. }+ V& f 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
0 a6 F& H+ A. T" f, o5 Xva_end(argp); /*结束可变参数获取*/+ h, m& P$ ]; W3 W8 z
return ;, e% i6 b( H. z5 a# P+ v
}
$ {4 N7 _( S5 M) g6 t! V& b( t% C5 N
4)可变参数的使用需要注意的问题6 v* z% n& ]& Y/ s4 A
5 n) K0 O3 ?/ t' \1.每个函数的可变参数至多有一个.
9 a9 P8 A( G9 f! j8 Y$ X4 d+ O2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
6 | W/ y: K/ p3.可变参数的个数不确定,完全由程序约定., W) I& c/ d7 _ `4 n7 ]
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.4 g& y k% U/ N: Z3 e9 Z3 A
而printf()中不是实现了识别参数吗?那是因为函数
# I9 M4 {- q, X" Q. hprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
2 f& e. Z( @( `- Z0 d7 K的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 8 {7 b8 U5 t) Q! e2 ?# |7 d1 Y
过在自己的程序里作判断来实现的. 9 H m4 Y/ I; B! r' k
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|