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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
6 H( V& i5 I4 S8 g, k) ?它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
: K/ O: O) {" |* ], w可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
! H9 ^7 p) k- @! E* ?3 h1 r定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有# }3 U4 n0 j- R( P) i
实际的名称与之相对应.
$ |' k9 d' `& F" ]" ~ ?由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.8 u3 W- n0 z R" q) g6 [+ I7 }
然而,更多地自由,同样也加大操作上的难度.! P/ e. O- Q( A
以下就对可变参数的几个方面作一定的介绍.9 y4 \+ E: G% M( K: w
7 m' V% O$ b, }' W* g1)可变参数的存储形式.
6 L% h# C+ Q7 Y# U
2 M; p% i$ L5 X6 f: i% C8 M大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,: R; h/ T( D1 r9 T
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
, W5 q4 |9 y+ \5 I! }) S在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
1 ~7 I6 S" p8 n2 f& J2 D这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
" \# L6 \6 |4 ^因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):% f% T* K) o9 E: l" F9 Y! r
栈区:
+ I. \3 i: w, i; {; i* @7 t. W% x
|栈顶 低地址: `! d3 t* s. E Y& q& U' Z! `% w
; T" l8 w1 C1 E$ W# p6 w|第一个固定参数var1/ Y! \, K* d2 E; i
|可变参数前的第一个固定参数var22 K$ q8 V# k! ~; G! }# f
|可变参数的第一个参数
- }2 E3 G# e% s u! u9 l I _|...
8 u* F1 Z9 T0 M% I|可变参数的最后一个参数
9 y7 p7 ~2 v+ z|函数的倒数第二个固定参数var3% ~ n( @* u8 x+ a
|函数的最后一个固定参数var48 \' d8 g9 S- o
|...; Y" k. c9 d- p1 T! K V
|函数的返回地址
& a0 ?! z7 Q0 ?. m' V! Q|...
" @3 [0 t) x. N2 K6 A|栈底 高地址) y! E. G9 f" m
( p: s' @+ e$ m* M; T9 Q
2)使用可变参数所用到头文件和相关宏说明
4 F5 r9 h& o' U: }4 I" g6 Y- `$ a7 h6 D
在此,以TC2.0编译器为参考对象来说明.) G0 Z4 r5 b9 h, ]# d/ ]
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.9 n" g" l1 Q4 a2 d
此文件为:
% J, p+ p x4 i. n# f) d/* stdarg.h8 T. J) }0 [5 M2 S
+ R6 J6 c3 j" r4 a+ M
Definitions for ACCESSing parameters in functions that accept
3 J7 {' n* x% M1 @a variable number of arguments.4 M9 j! G* l0 a2 \1 l% @
" x" `- R, j6 _8 j! ]/ E# X6 r
Copyright (c) Borland International 1987,1988
/ G4 s: O8 z, g; L! jAll Rights Reserved.
( |& i" r8 J) F*/0 b, V, k" x' i( n# [5 J
#if __STDC__( H9 D" W% [1 N5 O' X& w
#define _Cdecl
: k; N6 e+ `% r: W" z* z#else" B4 v1 m% Y9 b9 `
#define _Cdecl cdecl
) \6 y' u3 T) u9 Q, l5 u5 {# o& m; [#endif
# v9 m9 y \5 t% u( z. c& Q
: d2 ^9 e% M3 s' n9 Y) `#if !defined(__STDARG)' W# ~8 [3 c c
#define __STDARG
! R" _ l6 h4 ^: j7 v& { G/ T$ x: F2 _5 K
typedef void *va_list;3 A* J, {( J- B: @' e' Y `( L% U
9 D! o8 b- ]" G( m( J#define va_start(ap, parmN) (ap = ...)# H) i! ]' J& }' d) V+ h T7 T
#define va_arg(ap, type) (*((type *)(ap))++)% d ?& z+ i- x I0 K. q) t
#define va_end(ap)
, ~- |! I0 [: w* _6 _#define _va_ptr (...)
. e3 c) Z2 v6 y# J& R9 y#endif
9 ]2 ~( }. f! }+ J0 k2 r- G# C0 k2 U# W# F! D. K
以上为"STDARG.H"的内容.. Q3 Y/ ~0 r( C
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;& _, h2 Y) X) P. F# b
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,6 {: [' b. k) R# s1 L8 B; Q
parmN为可变参数的前面一个固定参数.
; U0 M9 w* _% {& qva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.- d! l0 S5 X8 k4 S
va_end(ap) 结束可变参数获取.
$ M5 W; k& v# i( D& B5 A( f- |6 ]+ Y A- g' _
3)可变参数的使用实例
; _) f) s" d3 U$ `5 T+ H' _6 Z J$ t' X* W) S, \8 C
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
& E$ P+ o6 @" R! e n
2 \ n: c7 u" \# }# m#include<stdio.h>9 U" I* |$ U; ?: l6 k
#include<conio.h>0 T2 }: @+ I/ x: Z+ }
#include<stdarg.h>
; p9 S4 A5 C0 [, o0 _6 P/ Qvoid tVarArg(int num,...);/*num为可变参数的个数*/. v+ Q B6 `) }/ t- `
int main(void)1 l$ k! @) I- a. _' i. q& S
{. s6 s9 x3 U- g2 S( R/ k! X# v& H
clrscr();
R& `! ]: K+ m* B2 E: G. g: o5 utVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
# X, X% N" L$ e7 Z/ h2 R; ntVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
% l; U+ j6 j0 E% ]& Wgetch();
. J9 F$ S/ ?; V: w7 jreturn 0;! _4 J: E# H; C
}
/ N" f# [# X+ R4 ]0 [. y1 |void tVarArg(int num,...)
, D2 h {5 m2 a{/ e @6 X- q& [. H/ k4 i6 A
va_list argp; /*定义一个指向可变参数的变量*/
, Z8 j8 j1 F$ a% G/ b8 U( y/ m7 M9 Cva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
9 D. M7 S9 Y4 Y3 W# {7 {while(--num>=0)+ I' P k, N H) i0 C
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
& b' j* c' Y7 N9 e0 U" t3 | 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
, V" Z2 ]. i1 P0 \) m- ~# k( {va_end(argp); /*结束可变参数获取*/
0 c' ^* b/ F0 t% L1 X" x4 j5 m6 \1 Greturn ;
; S1 Y p) a) \ J* r0 P: z0 g}0 V9 @' U. S( U+ D. Y0 {, R5 a
" `( R$ A+ J5 x4 C+ g. A
4)可变参数的使用需要注意的问题) y( S8 x. y; c& u: B* |, z, u
0 x% @/ `7 D. j* q7 _* a1.每个函数的可变参数至多有一个.
$ M7 {; u o6 h# v& Y( ~ K- `9 u2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.+ V6 Y3 Z; U) p: c
3.可变参数的个数不确定,完全由程序约定.7 _% p. U5 K0 {" V6 c
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.5 n8 r9 M! f, R0 F) t
而printf()中不是实现了识别参数吗?那是因为函数 4 m) z0 {2 h/ s- X& Y6 P
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
5 A3 m- V/ G r0 g8 E, G4 ^的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
- G0 j* v3 F& C0 ]过在自己的程序里作判断来实现的.
[' K# O9 w, I: ?* ]( d5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|