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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
& a- j0 d* E1 s5 s& l6 t! b它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
0 k, ^! g H2 J* [2 M( h可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
; X; p, R5 z9 o( W. m- ?定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有* y3 M6 `! U! W
实际的名称与之相对应.
: W. W- _7 t3 p8 H" l由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.) P) Q. F; s1 X9 a
然而,更多地自由,同样也加大操作上的难度.+ n. [: ]' b7 e0 Y4 H
以下就对可变参数的几个方面作一定的介绍.
6 t Q* G* Q- o/ y9 J0 |
/ \2 Z( \3 T( C9 x+ j1)可变参数的存储形式.
6 [+ \7 r; M$ V/ y( L0 s9 N
% ~: `5 F6 Z( N5 M大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
1 u* D) _- T7 P1 }9 a存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区., g5 K/ T$ b ?5 W$ j$ l
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,' S& t: y1 r- q( v# O
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.) Q$ }6 B, \) h* i! ^9 Q
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
6 j% N' @; |2 Q0 M- q+ y栈区:7 M9 q% Z1 [% P. }6 K8 J: h8 o
% \ k& y& R4 c1 Y9 B& y
|栈顶 低地址
+ ?' q( f5 k! E. Q8 N8 l0 H5 T: @7 `) A' f; {1 w. _
|第一个固定参数var1$ V0 }5 b+ |" c! O( x& ~, V' w
|可变参数前的第一个固定参数var2, k9 J' R- j. T7 C* B% C
|可变参数的第一个参数9 Y3 A0 L& j3 R: K
|...
9 H# I) \) p3 ]/ t! a|可变参数的最后一个参数
! u( b7 B+ E& d) n|函数的倒数第二个固定参数var3
+ J+ Z- _- o) ]- e/ X|函数的最后一个固定参数var4+ V- _; N% t! A, b( v3 n
|...
, _$ \7 a+ [/ p2 j5 U) D# B7 T, i|函数的返回地址
{4 G" b' s9 x|...! O- M1 W5 w# _8 J( r; c0 }
|栈底 高地址
9 R* T2 T* i( _+ x' D# p9 P2 Y- Z3 |! H* p4 C; W8 u* p3 B7 K
2)使用可变参数所用到头文件和相关宏说明& [( U+ R/ F7 f0 \* x
2 n& l8 I7 k* O0 w在此,以TC2.0编译器为参考对象来说明.
7 t2 _8 D, { g+ l$ Z5 v可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
* B6 V8 {, j) [! K5 M此文件为:/ R+ Q) B; y! y' |
/* stdarg.h
$ x; @8 s0 {. `" G# C# X8 L& q ^0 [' L, n! N
Definitions for ACCESSing parameters in functions that accept+ S, v, `/ D" f
a variable number of arguments.) @7 |* X9 `, H* J
, Z5 [' s$ k# N5 k; S6 ?6 r
Copyright (c) Borland International 1987,1988: h4 B) _, y9 @% z
All Rights Reserved.1 o4 k5 i# N d0 E4 s1 j: h' p
*/
) E% I6 ?* N7 j( u K6 `#if __STDC__$ l) G# M p. Z5 }" t: [) @
#define _Cdecl
. ?4 v5 r# o" |3 F) V& O#else
1 R$ R9 `& X# r7 p4 x+ @5 q#define _Cdecl cdecl
9 V. F6 n2 }+ N2 ~#endif6 j" i) i" x- r$ b: l7 ], h- c
& S, y0 Z- A4 t# t' R3 j#if !defined(__STDARG)
' F0 I) M$ H& M3 S8 ]# @* W, Y6 }#define __STDARG4 O& D0 S6 V/ e
6 T/ J: z& k! }9 K3 xtypedef void *va_list;# q p* H$ z, n3 P4 b
6 X6 M" C! W% _4 ?- g+ m8 i#define va_start(ap, parmN) (ap = ...)* e. L9 H$ ?! u) s
#define va_arg(ap, type) (*((type *)(ap))++)
3 R7 | N% _, U6 L0 S O#define va_end(ap)# D# f7 l) `! z e
#define _va_ptr (...)
0 b# I6 o& ~4 L$ a) G, a#endif& G- Y- [0 C/ F. b! \
. {! g1 K' C1 J* c
以上为"STDARG.H"的内容.1 Y) v& W7 `2 R
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
$ [. X d$ q" e+ F( \, R# R3 O }va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
% d7 w! \0 K7 m: z+ g& R2 v6 P5 J6 VparmN为可变参数的前面一个固定参数.
1 p9 a- F* G. rva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
: `8 o$ N, B" _2 ~7 lva_end(ap) 结束可变参数获取.
d9 s" W9 `0 U |% S4 r8 { ^1 A- u0 m+ X" v
3)可变参数的使用实例
3 D, V5 t9 g$ p6 u! ^; I2 C
# P8 ?3 j5 _( h9 M9 Q实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
; y. C5 v# ~8 D9 B" Z/ x* S% q; P8 |7 b
#include<stdio.h> y( T& c# U+ A( i9 Q3 O, ]
#include<conio.h>: F- y j2 L! |6 E
#include<stdarg.h>8 Y) _* T6 p: }7 l7 J
void tVarArg(int num,...);/*num为可变参数的个数*/
0 o' E0 c8 d( Bint main(void)
. g7 `/ K6 y# l{) `; g( c" F+ Z& `
clrscr();! ?5 g8 A# @0 {, D, ?
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");$ d$ G! g: h; O$ F' p
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");5 T5 _; t3 u. W$ Z4 C8 S& f& \3 c6 i
getch();
$ f: U8 }8 z2 d+ w/ y% d" V6 Kreturn 0;! Q$ q* p7 P& E# w# z
}8 J4 \3 G V7 E# k& _4 b
void tVarArg(int num,...)' y. g" d; _: U9 H0 d/ Y! y
{
: d3 L2 @7 P/ k; b, s+ nva_list argp; /*定义一个指向可变参数的变量*/
5 c- d" a6 b7 m4 t. vva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/- I a% `7 h( _ z7 l" f; E
while(--num>=0)! ^2 B9 F! `. B& Q7 A3 F
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,3 O# J+ `4 S" B# x& a1 p
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
# T2 A, F4 G" E" q/ Z4 x2 @va_end(argp); /*结束可变参数获取*/
2 V% S" m$ f7 e$ ireturn ;0 P) e: d0 Y5 L0 \2 e1 P1 L5 e/ v( ~
}
3 s$ w# F f2 Y* i f" ~1 E/ {/ W8 T/ K7 M% Q
4)可变参数的使用需要注意的问题
! \: u$ _/ {* l4 i. Q* A9 p3 @5 t x( W: ^
1.每个函数的可变参数至多有一个.
% a9 O4 M$ K, U0 o2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.! E! d7 S: r! R! I$ `, V
3.可变参数的个数不确定,完全由程序约定.
3 [" [7 {; ` R0 W& v" E4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.9 ]7 i$ v9 ]1 i' R. F; B9 V6 _! s
而printf()中不是实现了识别参数吗?那是因为函数
* p! n- [3 C" O" p! s" \printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg @, q% g# W6 K& L7 r
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
/ {: f4 b9 w" Z3 V" d3 y! H. e过在自己的程序里作判断来实现的.
6 x# _. U( S/ S0 W5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|