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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.' G1 w9 n- n% O3 _9 Y; c; s, Y
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.8 h; V$ ~0 O$ L8 d8 z5 d, f) X
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
) S }; v. O7 ?/ u, B& O6 S定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有8 X8 U1 R* n1 x+ U0 k7 f
实际的名称与之相对应.# W( @, w$ [' I% G: p3 ~; Q
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
. P4 u5 E$ P9 d6 N, M$ I+ e然而,更多地自由,同样也加大操作上的难度./ e: N$ i* r; x, Z6 [
以下就对可变参数的几个方面作一定的介绍.! `' W6 }6 s& ]- r5 `( ^2 d" Z
( l7 i6 `" a/ j3 k9 X$ d
1)可变参数的存储形式.& s3 w7 t2 M' q V4 x# {- y
( L7 ]! E6 I& s& S
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,% R7 ~9 h1 r/ X
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
6 @# N8 w" l1 \5 Z" c5 @3 a; N在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
' L V$ g" C( x1 j w这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.# j$ [2 W4 L) k) f
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
- X! F, z, d) ^( [# h: J栈区:6 S4 D' H# s7 s6 A7 ^
0 {0 ^# I; O' t+ i7 d4 f( n+ u|栈顶 低地址! m- f: ^8 e1 }# ?$ w4 D( N5 ~
, t0 k/ h# W( b|第一个固定参数var1
" _+ _# p- m3 c% J: L7 K; I, r" T|可变参数前的第一个固定参数var26 p8 s; i8 b3 r9 P3 W( e1 h5 J
|可变参数的第一个参数/ N; G* o8 O( ^; x% G
|...4 G; ?5 I1 f9 {; R
|可变参数的最后一个参数. u, O; U8 u1 ?% T3 D0 V
|函数的倒数第二个固定参数var3
; F4 J. ~# ^$ e( D- M|函数的最后一个固定参数var47 c% y' l- g9 V- L
|...
, B/ o7 N9 l" T8 h|函数的返回地址
5 S v2 L0 p/ R/ f' d|...
) E7 z+ b6 ?: ]' X7 h: S|栈底 高地址
" C& x8 o3 U2 A5 n1 X$ I& Z4 h6 E1 f5 `5 E& `3 @2 I5 o5 D" P
2)使用可变参数所用到头文件和相关宏说明0 O+ \6 q! W8 z0 P
: t+ V' l1 ^: z& b" S
在此,以TC2.0编译器为参考对象来说明.
( e0 y1 I4 z9 R0 E' [可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
K1 r, A- \5 s" X$ g此文件为:/ L! a9 g( ]8 g4 Q! E6 b$ b- o
/* stdarg.h' ^5 n9 a6 a% B$ g* |9 e! ?
6 P8 ]/ t$ s$ J) u/ m4 nDefinitions for ACCESSing parameters in functions that accept
" [% C0 p$ d7 `: n+ M) h5 f) Xa variable number of arguments.( j! U: Y; _0 q5 m
7 ]1 F, y) J( B5 {Copyright (c) Borland International 1987,1988: G. ]6 A+ O- @
All Rights Reserved.! I O. j* D2 w2 i$ v& c0 U
*/+ x" A# e0 L( T1 P% R6 n+ \
#if __STDC__% f' V+ n; h: b! m( N
#define _Cdecl* J) L9 y- H# A) A' n7 C8 B
#else
% J7 n9 b$ I r& A#define _Cdecl cdecl) D0 Z8 V Z4 P; `
#endif b1 D: A* i4 ]; K) W: x% f, z0 q
. K5 s1 m3 Z$ J! R; R; c1 g
#if !defined(__STDARG)) b5 U3 ~ p- ^5 I. @# W
#define __STDARG3 `4 r3 d; y T0 M2 A
6 ]$ o2 Y5 ?/ q+ z+ K/ D: j0 ftypedef void *va_list;
: S, o# d) u: G% W( i [; _: E. V& W8 T
#define va_start(ap, parmN) (ap = ...)9 M6 {. q1 k0 m2 _$ w
#define va_arg(ap, type) (*((type *)(ap))++)
2 K4 h$ y) K) {0 \#define va_end(ap)
; a+ Q( h1 K! D* z3 h- S2 M$ f( e" E#define _va_ptr (...)1 z/ L. G$ Q- x7 K* F, S" d
#endif
2 a4 E7 [) O0 b9 P: t" Q% s
- M- i& Z! x2 k4 v7 p以上为"STDARG.H"的内容.4 F8 x( B: |) p2 K
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;6 X! o- h0 K; F. U( H: L
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,) j+ B6 w/ b/ D
parmN为可变参数的前面一个固定参数.
8 a) d9 {4 X' @2 a+ y% ?! b! xva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.1 R; J8 P3 T9 I6 l
va_end(ap) 结束可变参数获取. K* ~. i' Y( j5 y! Q* \2 M
% U5 T1 r6 }7 k9 p3)可变参数的使用实例, G3 Q* G# t3 ~; l: s
; A1 c5 _4 f4 m/ C% |2 |实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
( g0 U# ?0 w1 y# [9 @" m. C
9 T/ n- ~% q6 u5 U#include<stdio.h>7 Y: o7 z& g% O( R' H0 k
#include<conio.h>
' t. @& Z, M4 u* z, u0 o7 F#include<stdarg.h>. j% q d: ^& O% m) m
void tVarArg(int num,...);/*num为可变参数的个数*/0 [5 j* _5 |1 Q; X. u( _
int main(void)
$ g8 q. v( U8 E" _3 |{& }0 E$ F! h e2 J1 B: ^
clrscr();, n6 n& r" T+ I+ v4 p
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
/ h' y) e; q9 Q5 b A6 E* WtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");2 g* n3 U0 {8 `, |$ d9 u V& z6 r) p
getch();
. v5 o8 Y0 {. s" B- `& Nreturn 0;
+ b3 z/ l: D' l' K7 w$ a7 i, e# Z}. y0 P" c* }6 u' N5 j
void tVarArg(int num,...)! [" j& s" {* z1 D; `
{) G. L6 V6 i& I0 a3 O
va_list argp; /*定义一个指向可变参数的变量*/
% z* W9 {! d# L2 E+ m2 s9 Lva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/7 s6 Q9 C( B; j$ T) U! m
while(--num>=0)* V$ ]( F! J, D' o( h0 |8 N
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
! V; Q4 b6 I" w+ G: {2 a 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
% g6 \5 r6 J+ D0 Vva_end(argp); /*结束可变参数获取*/: @- ?2 ^: s( W( Y1 t/ H- u8 n0 E
return ;
' R2 c7 B* ^! o H0 x* l- ?}( w9 p j* r1 d- X) q* Q
+ h2 {& x5 u/ L4 s
4)可变参数的使用需要注意的问题
( o' w# v# e( u5 v% P9 y
$ y5 ^. C! U9 {- T! y1.每个函数的可变参数至多有一个.3 @* J! o+ H' E$ r) E) Y; J
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数., U( w K4 G' K! `
3.可变参数的个数不确定,完全由程序约定.
a5 M i3 h4 J4 A1 q: m" @4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
6 w1 c1 P1 w9 H' q- X而printf()中不是实现了识别参数吗?那是因为函数
- w" H9 l, }$ m. k3 M7 J Vprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
V {2 Y A' S$ v4 w' s的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
) R3 u2 v& @3 z过在自己的程序里作判断来实现的.
( | U5 H9 B* u5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|