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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.- X" O* S8 M6 D& f6 @) V ]6 h
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.! K0 W0 T3 S( X2 w8 p
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不. ?0 k6 c) M0 D" W% h( H! q" Z
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
; P4 @; |: z; }3 K: Q实际的名称与之相对应.
; t( n, U8 C' F$ K) P1 B; r由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.0 r$ v- {9 l) E. O, N
然而,更多地自由,同样也加大操作上的难度.: v9 R$ x8 n' V3 K" H% Y
以下就对可变参数的几个方面作一定的介绍.
2 ^5 ?/ L" z' K% u
, d. G: I* U; t4 Z4 {% l+ R1)可变参数的存储形式." G& m5 p5 i+ K
! e: k" b; F. R2 f- Z大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
/ r: _8 }- R, G& i2 Y存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
6 d* _4 W7 `% M4 P/ w { x! f5 M在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,8 S% m9 D' F" F+ T2 h% T
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
9 L0 N7 e- i1 _! z5 l$ G因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):2 c: O2 U7 z$ F" Y' f
栈区:
, u" f: T8 ?3 p/ B' o+ ^* P8 S5 u* o
|栈顶 低地址+ @0 C7 f9 Y. t0 q0 M
/ b- K4 M* O8 d# }) ?
|第一个固定参数var1
! Z) V: f$ C" E* y# g|可变参数前的第一个固定参数var2* m8 {) z; @9 R7 {% m
|可变参数的第一个参数8 x, g6 I$ M k* C( R
|...
' I! D4 p% f8 @" Q|可变参数的最后一个参数
9 v7 D9 x! Q y|函数的倒数第二个固定参数var3
" n# o' @8 ], s" @& ?% k, w: T* }. E|函数的最后一个固定参数var4
, v% Q8 h3 D& E2 [# S& @8 q, f/ v|...2 b7 C% Z+ a8 Y2 N
|函数的返回地址7 G: l7 U; ^3 n7 \
|...
( J$ l% N2 e: r- j5 a s8 g9 p0 @|栈底 高地址
/ D* \: C: {6 o1 i7 N! e1 z. d! w$ J. X; Y6 b5 B
2)使用可变参数所用到头文件和相关宏说明6 C1 s( ^- _; G- I4 [
" q1 p/ ~# O! e/ s8 w3 z% {
在此,以TC2.0编译器为参考对象来说明.: V$ N& J! ~1 e( r
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
& h& K/ m' M4 v K此文件为:
+ L2 R8 Z, } g Z% E/* stdarg.h( S: S( |, S8 F
1 C2 ]0 n7 w) w. t! q2 N \Definitions for ACCESSing parameters in functions that accept* b# P: k1 @* x; K
a variable number of arguments. W( K& M! b( `5 C$ @* J
) d9 a/ s" j2 e* m5 t" c, M, KCopyright (c) Borland International 1987,1988
p0 C6 [9 i: v9 {5 o+ VAll Rights Reserved.
/ \( L$ }, S; `6 k*/) q5 q/ J* W0 W0 ?
#if __STDC__, f9 j4 j0 I I7 V
#define _Cdecl6 Z7 D7 P1 ?4 X5 U, I/ s9 u: h
#else9 n; g" i$ L# s6 n" [1 j( s; Q
#define _Cdecl cdecl" u' t2 t" J& H
#endif2 U P& _8 l* D" j j' y
9 X6 H7 a% ~$ f& |' G1 _$ x2 n#if !defined(__STDARG)/ w5 S- K1 a6 W4 q, `: d
#define __STDARG9 O2 j7 O( c3 C3 [3 _
% i! b2 S, C; A, g& x0 K2 }' Utypedef void *va_list;& z, F+ N& |8 J7 l
1 v) Y `# n( A" n2 I
#define va_start(ap, parmN) (ap = ...)1 {7 Y( c- | `" [: g) B3 P
#define va_arg(ap, type) (*((type *)(ap))++)
4 m& {0 U4 z7 \#define va_end(ap) b7 c+ G7 `3 b& {% \* q K
#define _va_ptr (...)
. V6 S: Q: U2 |3 [7 l6 J0 ^- @#endif* p3 Q3 h: `* [7 [/ v N$ e4 {" p
7 p0 }' a4 ]" T0 {' O- j6 B9 b以上为"STDARG.H"的内容.
7 J; m4 a6 X) i* ?+ D8 y, o2 }该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;/ G% i3 G0 }* }, R
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,5 z" t6 @# E7 _
parmN为可变参数的前面一个固定参数.
# R# h$ [- ^2 E0 v. wva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
9 |$ L+ \% B. b$ B9 h$ L" C2 Iva_end(ap) 结束可变参数获取.* H0 c( N# N, |( q7 B
; v6 c" S; j; I# ?6 O6 |3)可变参数的使用实例; e0 n( Q' T2 V
: D* q7 q7 H! t, w实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
! I4 |/ L% A$ `# N" q! y
' ?) U2 ^+ c6 S) H" B0 g$ @- f#include<stdio.h>
( w5 H' P6 w3 m0 G4 Y! D2 b#include<conio.h>
* d( x2 M' X2 S% M#include<stdarg.h>
4 f+ Y3 N% {2 X, svoid tVarArg(int num,...);/*num为可变参数的个数*/: _ i" n) K9 V4 u2 {
int main(void)
; p- a0 J7 X+ k6 L& D* ?; h{# U7 Y K& v2 P
clrscr(); A2 o. f9 f: T! \
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
1 D1 C1 V; s$ P ~1 |4 [" JtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
8 q$ _. y, t# o, K ^3 Y0 o) Ugetch();% D ^8 z+ H9 f
return 0;. u2 {2 ?) p9 _) C2 E9 O. l5 ~) [* e
}6 i5 j/ t0 ], f* F, y
void tVarArg(int num,...)
6 T7 j4 z7 o8 Q% J7 |( D; R{
( W+ `- G; U# |4 ]2 ` {va_list argp; /*定义一个指向可变参数的变量*/! _$ e' {2 | G5 m- \" C: [
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
5 o) Q' x% z# T8 ~) F2 m1 Kwhile(--num>=0)
2 e5 h6 q9 ~3 A printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
- z7 [) H: F7 y8 |: ?0 L9 a; M 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/8 Q$ \' u9 `5 j7 P
va_end(argp); /*结束可变参数获取*/
/ Z$ ?1 `0 b8 zreturn ;
/ i* Y& ~/ d& P# y6 \: s}" a' ?4 D- p# |$ D
# X' U* H! [8 l4)可变参数的使用需要注意的问题
7 G: O% j: M6 p9 z! N8 L3 z
( q( [: [) L' i; ^! D3 f' ^1.每个函数的可变参数至多有一个.0 [9 ?3 q+ \7 D& M2 P
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
! C7 b4 K. y, G+ r. Y3.可变参数的个数不确定,完全由程序约定.
3 X/ W0 p: ] \% V8 j8 y1 H6 Y4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.+ |1 r' h! g5 f) M
而printf()中不是实现了识别参数吗?那是因为函数
5 g, n @( w) `) tprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
; V8 M% K0 u% Z2 I) n" ?的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
3 x* p, a% h6 V2 ^' _ ]2 F K( s过在自己的程序里作判断来实现的.
4 s/ f4 X# K. V* R4 d5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|