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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.4 M$ p, I$ v7 _: E* \
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
! a: X& _- J5 O0 y% W2 A0 J8 a可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
5 |3 k; ]8 }0 o* G# R6 l, t定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
1 f1 `& T* v' U+ b! `实际的名称与之相对应. _/ ?, ?5 R2 r& O
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
6 R' z9 H; j" H% R+ N然而,更多地自由,同样也加大操作上的难度.+ `2 j7 l' n2 T# E
以下就对可变参数的几个方面作一定的介绍.
! e9 l! f7 x7 e$ ?) p7 Q% O: b+ V
; h3 v6 n/ {# o. c. C5 ]: c1)可变参数的存储形式., H7 Y( `# ^. |+ v) M. ]* `' c6 [
+ f. }. A8 N d! o+ X大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,$ y$ x" G0 y( \ K3 P& b, _
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
' y+ F' @# a4 {1 v; P; z2 v: p1 g在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,; Y; n, {$ X& J+ q; P
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.# {6 V$ j* ?- J9 S' W
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):' Y& } t- i2 m+ B1 _& m7 u% D; g. U
栈区:
/ B- T% \. ?1 @. {4 y' k0 v$ ~. m" Y( W. V+ i- z
|栈顶 低地址) H( k$ L$ H' r& D" B
B% D. J4 z0 ?+ ^: ^& E7 E|第一个固定参数var1
8 ~* F [3 V$ ~- x" @' p|可变参数前的第一个固定参数var2
$ V+ k+ a6 T- `; t|可变参数的第一个参数1 @' c7 Y1 N4 `! S, i
|...: @! F% J( g$ [# M( m+ z
|可变参数的最后一个参数
% N$ m! t% x/ f5 b|函数的倒数第二个固定参数var3
! P4 G2 D P& |! Z Q# q|函数的最后一个固定参数var4/ U. P8 _8 Q2 G- S
|...
: P9 v) `5 |# v+ U! q|函数的返回地址6 w% G3 T* F: H/ J6 a, [" T. I3 o* D# I
|...5 q f X! m8 V0 ?$ n
|栈底 高地址
2 q; k( x }5 J. Q+ r6 p' O+ u) @$ I' l! ? b; |) u# O
2)使用可变参数所用到头文件和相关宏说明1 O' T4 \" P0 L4 F0 v9 J/ {
( u, p$ d/ J6 A6 \+ s, C& N% \* ~
在此,以TC2.0编译器为参考对象来说明.
3 M3 ]# i; t: K% Y8 m6 i; x- c可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.4 Z$ A5 A) @6 \' f! h
此文件为:
: l% V: S: M7 I" b9 O% e) \- o/* stdarg.h3 z8 }0 r. E$ a) t: t
4 e9 A/ h& a4 G
Definitions for ACCESSing parameters in functions that accept* ^( s- x* O9 M) T8 ]
a variable number of arguments.
# Q- S8 X2 G' x3 P
! b! E& Q( A4 K* oCopyright (c) Borland International 1987,1988
% c8 O+ f( G% C {All Rights Reserved.
- h: X# _7 ]) a; A+ U! v8 P- ~$ Y*/
# \' l- N% i- O% I/ i#if __STDC__
0 w# Q0 K1 L C#define _Cdecl
$ O9 D. V0 w6 \0 f, i' z5 [4 `+ X#else- G8 [% `* |: }/ u' x" e
#define _Cdecl cdecl
% M5 I& s. N! h }8 k#endif
+ a; i0 S9 U) l& `4 T- C
) [% [0 _# W3 d5 a0 {. [+ ~5 I#if !defined(__STDARG)/ s- C& }- o& V/ E
#define __STDARG6 Q* s6 A' s7 P: _' w$ O( \
0 f, f' M6 A- a* B' ^0 _# ]3 d% `
typedef void *va_list;& q) N9 o3 F* j, E1 B
; e: L9 v( Q* M+ n+ d2 ?( e, d% V) J#define va_start(ap, parmN) (ap = ...) W) P$ D; Z2 J2 `
#define va_arg(ap, type) (*((type *)(ap))++)( ]) L% f6 T. p
#define va_end(ap)6 J0 }+ z, w- f/ k9 K
#define _va_ptr (...)2 B9 J0 E& l( f' x
#endif
6 B0 G% h1 x: b$ ^; x( C: w' V& L- _9 M7 j
以上为"STDARG.H"的内容.
4 x* S& v; S b: X该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;" J# `/ P% m0 N( x: M
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
: G l$ F. }9 H+ DparmN为可变参数的前面一个固定参数.
( l( k6 Y+ R! x' R) v7 Z) _$ Ova_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.' b1 S$ H. y/ p, u0 X2 _% s6 d( a
va_end(ap) 结束可变参数获取.( @) X' N/ T& F) R3 t* S# c1 E& j
6 A8 m H/ ^! \ m9 h4 n6 W3)可变参数的使用实例
& _, Z3 j( j+ [2 {
+ l1 I3 S( [: O# _实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
6 E5 H0 A w) D, Y' x: t
T* Q5 C+ H$ R5 k- u#include<stdio.h>
b+ k0 S6 a3 a, d4 B9 M# C" O#include<conio.h>9 S8 ?; `% W7 G' H! \# V
#include<stdarg.h>% p0 ^/ F# h/ ~& Z
void tVarArg(int num,...);/*num为可变参数的个数*/( J3 V4 A" [' S
int main(void)% E5 M; g+ c3 h* z% @, e" a
{9 @4 n, O0 d M. P/ c5 J4 K! A [
clrscr();
a( s8 @4 p9 U# y, m7 ^8 rtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
0 Q/ i k4 @3 Z- K9 q: I G/ _tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");5 F1 f4 |- ~& l" `% p+ I# D
getch();4 Y" ]) m# d' Q# B
return 0;; t+ W$ O6 b- `+ ?& d3 _- i
}. P P6 ~# N1 f _# t
void tVarArg(int num,...)6 A! L3 V8 o2 _3 p6 s
{5 Q2 a2 i9 V2 n) g2 J! P+ ^& z) o
va_list argp; /*定义一个指向可变参数的变量*/, H9 N( x) O$ t6 U7 [
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/ Q4 h! U: p8 i* u+ H$ f
while(--num>=0)/ L8 U: o! w# s6 e. U
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,) e' K _0 j3 e1 ]+ B3 [' y5 [7 ^
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
# F* q( O/ n- x4 T4 w0 Uva_end(argp); /*结束可变参数获取*/
" z4 n$ i- d4 dreturn ;
, H# B4 S4 C& C- |4 A+ [}+ P9 `; M1 C9 \, \3 _) y& W
1 ~) l2 [& L5 [+ n7 G( ]" w0 Q/ h! k) v4)可变参数的使用需要注意的问题# I; v- J* C+ _5 o% Q- `% j
, `4 n: F' q8 I" q1 I. u/ r; ]1.每个函数的可变参数至多有一个.
+ {3 X. o; L! M1 M2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.) L' j: C! ~5 Q' r4 w) R z
3.可变参数的个数不确定,完全由程序约定.7 V8 w x7 _7 J, M
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
, @: `% Y9 w2 h- Z. I" q. ]& S而printf()中不是实现了识别参数吗?那是因为函数 ) u, z: `3 F9 X: v+ c& ]- G
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 2 H6 W+ [& Z' }7 p* B. a8 ?
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 " y9 O' e6 X8 K
过在自己的程序里作判断来实现的. 4 L w. g* Q/ e- `5 h8 _* O+ Y8 {
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|