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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
. X1 l, V6 }. j" d9 D它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
7 J7 g. @! p/ J1 U: A M可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不2 C7 O2 y* y: h3 R
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
: y& {/ b3 n& o实际的名称与之相对应. M/ s! ], F( @9 q4 @$ V$ i
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.8 @. ^) V E' L0 I/ g
然而,更多地自由,同样也加大操作上的难度.9 a; @1 H; L; D/ C( }( ]
以下就对可变参数的几个方面作一定的介绍.
( A5 j. u3 g6 W/ f
6 }5 H5 s- Y8 U1 O2 Q8 c1)可变参数的存储形式.
* Q4 G- t$ P! Z, g! y
c( U" v5 j# u) ]大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,3 d) u" }. }; y2 _# Y9 t5 ]
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.5 ~- t v1 }% J1 E2 i' W% B; D# E0 E
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
8 @0 g7 x% f* r9 g0 I* u: t这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存." O5 y% Y. c+ d8 e" e; b+ ^0 P" w' i5 x
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):8 j! `% G, P2 o) R, {1 `
栈区:6 w! i4 @2 L2 W1 S8 D
! \ s- P; _" A& E4 {+ t|栈顶 低地址
$ n$ `* H# s* |5 |; M
% I' z7 g* h0 @; `/ ?( b3 u7 g|第一个固定参数var14 e. z1 Q& p/ V' X
|可变参数前的第一个固定参数var2
9 W7 E. N0 V# Y|可变参数的第一个参数
: E/ b- }3 ]7 ?# x! w|...) F" w" b0 o( n4 E' _$ D
|可变参数的最后一个参数
9 e* x6 J0 H% O|函数的倒数第二个固定参数var37 R: [7 N$ c; A: \3 B, c5 Z& {
|函数的最后一个固定参数var4& O) L% [4 W; ~5 E9 p6 _, y
|...
^; s$ z' Q0 j|函数的返回地址. y- F. U, n' u8 l: f8 {* G; ~
|...5 C* l4 j+ f, ]7 f
|栈底 高地址
7 w* I. p, q% x: }
/ R. M5 n* p+ g- l; o2)使用可变参数所用到头文件和相关宏说明
' E E! i1 d$ r- x; Q( q0 X/ J2 t7 H# X$ P' X( V
在此,以TC2.0编译器为参考对象来说明.
, q: W7 w& a/ W& B; P, G可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.! S* [. W z- O
此文件为:+ {8 A5 e: m0 F0 D( Y0 d- v1 M& J
/* stdarg.h8 \0 |. \8 ^" U, I8 z' _
5 R* ~, C9 v& h7 _8 h' W- bDefinitions for ACCESSing parameters in functions that accept1 i6 {* N J0 U: L1 l* C1 [( a9 Y: Q
a variable number of arguments.
9 B" k' `" d" n
6 Y% }! ?8 e7 _+ ~) WCopyright (c) Borland International 1987,1988
( V4 e; [! G; `4 X+ [ j; v/ ]: qAll Rights Reserved.: l8 R; c, q) R$ e( N
*/& p+ J) f! p; ?$ }: h
#if __STDC__
( E0 T. u& ]3 H* |8 G8 s$ c/ C* |#define _Cdecl4 y6 Y T5 z' z3 O
#else
: b3 k: ~. t; W& \; Y* x9 u7 D/ z8 }#define _Cdecl cdecl
/ A8 ?- f9 S8 C$ j9 y#endif) ~( d/ p, U2 [
b" _# E- d* O/ }#if !defined(__STDARG)7 o& P. M! q3 B4 T' |7 V
#define __STDARG
, T) K; i/ A' i' C/ ^4 N2 c) M, U5 u* o, m2 C5 r& W
typedef void *va_list;/ e3 \6 Z/ }' X2 G/ n: v$ _+ E/ N
0 j! n# }$ B9 A. S, ^4 }3 ^& {#define va_start(ap, parmN) (ap = ...)
7 S/ Z; r2 p* m7 B0 I4 s#define va_arg(ap, type) (*((type *)(ap))++)* s9 ?4 D. Z$ O) E+ j! L
#define va_end(ap)& y4 R: M f9 n5 ^$ o2 @
#define _va_ptr (...)
! |& {3 X; n" w3 g- w#endif/ f' f2 ^$ N2 T8 `: j+ I+ o$ R
* _1 U9 f+ q+ E, e$ c; k
以上为"STDARG.H"的内容./ \5 }0 L& f/ f+ @
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;7 R" B7 U. N# W' v2 I
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,9 E9 i& q) \4 b0 v" {
parmN为可变参数的前面一个固定参数.0 Z4 t6 m; x, l
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
& B) E4 g0 B% A8 o% |! pva_end(ap) 结束可变参数获取.
) n' w2 ?3 o. r* y0 C$ `
1 o0 A4 E/ j; c c7 Q3)可变参数的使用实例
" b' b7 c# J' q) d
$ E* _/ u8 o/ t/ t# i x实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.# M ~* G. I; ^4 g5 n8 ]
0 C6 c% a0 A: I! ~7 u
#include<stdio.h>; ]+ l( a: }3 K) p$ U9 y/ l
#include<conio.h>
. n3 n- M" ~: b+ W! g#include<stdarg.h>3 D) E @. k0 U; Z) X0 ]5 l
void tVarArg(int num,...);/*num为可变参数的个数*/- W4 p7 K" |3 W7 c
int main(void)0 G7 e0 C3 {: u# g4 s
{
n8 [+ e( W Dclrscr();
: m3 a8 `$ M1 GtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");, C3 S2 u: b; T
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
. P( z4 \8 A( g! t- }getch();, z. x7 p* c) f! l8 M$ i* T8 _
return 0;
: f- T# Y' F! h% X}: B' ?& b* n& P7 U
void tVarArg(int num,...)
/ |+ e E" S9 T- ~0 U) o1 g5 S) O0 g{
& @! u+ Q4 [* {& w! g- F+ |/ Yva_list argp; /*定义一个指向可变参数的变量*/; m2 e/ X/ X5 s$ |- y f
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/( K* Y; [% K8 C Z2 l$ |; Y* ~
while(--num>=0)
1 r, T8 v# B1 j- |0 G k' ~ printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
$ R5 M' z: `7 t u0 {3 U 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/# w' ^6 I8 E: y4 x+ k
va_end(argp); /*结束可变参数获取*/
* X2 i' [( B4 i8 N# L) Q8 ]; qreturn ;5 f3 h. [# U4 Z
}
! m3 F# S- T# A8 l8 X5 {" ^; B, k% r- Z! N; Y; b+ T- ~
4)可变参数的使用需要注意的问题
A9 i# f$ J [2 A
: |+ k! {; _, L0 h) n1.每个函数的可变参数至多有一个.
: H5 w+ {! R! n1 l2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.# K6 h! L, n t+ \
3.可变参数的个数不确定,完全由程序约定.
/ y9 p" c# |6 @- Y: \4 q. w4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.! H$ o8 V. v7 \, V* [
而printf()中不是实现了识别参数吗?那是因为函数 / o: A, m1 m3 t, ^- d
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 3 \+ I# d( T8 h& p) n5 x8 A# L& U
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 9 y$ T1 ?+ g$ G+ T7 l: y; n
过在自己的程序里作判断来实现的.
& ^* }5 T5 E# [ F m0 O7 w5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|