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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.7 F! E- M, y& t9 ~7 k5 ^
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
" B# w a1 Y6 } w1 f! k0 `; |可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不+ n6 X4 w) X5 P3 D
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
; d8 q# i! d+ k% W实际的名称与之相对应.* O: ? Q& Z3 E7 ?
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.8 H4 r9 J: I7 ~3 }+ J6 Q8 E
然而,更多地自由,同样也加大操作上的难度.
5 `0 e# P& b4 E3 p: f t以下就对可变参数的几个方面作一定的介绍.
" R4 O8 g+ b0 e5 |8 c+ `
+ V4 B9 [" F7 e( m7 M1)可变参数的存储形式.! ]8 w5 S4 K3 v' o/ t6 \- d2 L/ m
/ _& h9 w# n$ X. t大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
- W5 y. R& x# I' s& p. D7 V* r存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.( @; B0 c: R2 a1 t
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
, B2 `4 c( q! s5 b( ?$ I* C这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
2 e# a& N4 M, d. \6 @, A* c% ^因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
# G9 m) X6 B! R7 [; S5 H) }7 _栈区:
6 ]/ P. _* L8 Y! J7 U' B5 E4 r
# U: t9 d. J3 k( H|栈顶 低地址9 Z2 S- o% n( o% u: e
9 S; ^) z. Q. ^: ^5 J|第一个固定参数var1
' g) Z% u* X$ H7 P1 [|可变参数前的第一个固定参数var2; o- |8 o: g0 U; v5 s& [9 \
|可变参数的第一个参数. w) r# x' l* ^4 B a
|...% v/ ~6 _. ]' a$ T
|可变参数的最后一个参数$ r2 k) v% g6 y5 p. D1 Y* ` _3 _
|函数的倒数第二个固定参数var3
# s1 l; \5 {. V) v' h2 j/ e. I% O|函数的最后一个固定参数var4; q3 _ |$ O5 O4 w& T% z
|...+ M3 o+ k& W$ L# p4 G
|函数的返回地址
( d9 r8 Z+ J8 |. t' H! }. x& \|...- Q6 ?( X4 }+ u1 w4 |( T
|栈底 高地址
0 Y; a* ?+ I K3 [. M( g }/ e$ p1 f) y2 c- O
2)使用可变参数所用到头文件和相关宏说明% ^8 `) J- N' s
* e! ?: t' g0 I9 ~在此,以TC2.0编译器为参考对象来说明.2 Y1 ?$ g" Y# G2 _6 k
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.5 H0 B8 Z3 r4 N
此文件为:! D; F# y$ o# }# y
/* stdarg.h2 I8 U" S' Y5 F& ?
9 Z$ w" L' P! Y- h- H x: r( y
Definitions for ACCESSing parameters in functions that accept
, Y: G) X$ `% p, |3 k' wa variable number of arguments.4 ]& G/ e9 m* n
% c [* V7 U4 z& j- P+ V: d4 D
Copyright (c) Borland International 1987,1988. S2 v; C1 B( S+ F, v& h0 T
All Rights Reserved.1 | x+ u( S5 o7 \3 ~4 p) m
*/
4 ^5 q8 h4 c+ F& i( ]5 n) Z f#if __STDC__/ e9 n/ G2 \# W( N
#define _Cdecl2 u9 T# v7 k% c ~3 |" v8 q
#else9 e* I2 d5 \- W6 f) f. m' \1 \! S
#define _Cdecl cdecl* v8 Y" w4 k" t$ z u' t
#endif% B2 g! s5 H: m5 G
3 Z2 n5 n$ l7 |2 y1 a. D; _
#if !defined(__STDARG)( k$ S* o- M, Z* s) P7 O- d# F
#define __STDARG$ h/ e+ Y3 ~1 F0 o
9 x! P# M2 e6 ]3 G- h$ Z0 {$ F
typedef void *va_list;7 Q8 V9 i& \0 ^: J; Z3 h! S
- o. |# M d5 b. g* |3 e" S#define va_start(ap, parmN) (ap = ...)2 K% H& l& i: I
#define va_arg(ap, type) (*((type *)(ap))++)
3 u- e8 ]$ l% L0 @#define va_end(ap): X0 |, `- ~4 c3 x p
#define _va_ptr (...)' U: i! I! R2 K4 h) w8 i& g. L+ \
#endif2 ?/ H. f; Z, l% _; t, K$ f6 C
8 t* t/ z6 n0 j, A$ J$ n以上为"STDARG.H"的内容.
8 u, G0 p( {( `8 C: B: n该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;, N9 b* }' E1 e2 c0 Z, b R7 `
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,/ J6 |2 i7 l- h% g- N, C- \
parmN为可变参数的前面一个固定参数.9 F) _% v0 o+ c
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
0 k, m: I! Y& v5 [ Z0 s* Pva_end(ap) 结束可变参数获取.
9 E* f- _) `' ^4 g3 i: z/ L% e' l1 G$ C+ v" x! _# J3 S5 q
3)可变参数的使用实例
]4 O1 ]5 U7 q' G% j: l4 R# n3 j9 d$ y8 [4 M- E
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.0 ?9 r& {& q8 c/ z2 G
8 i6 h% H6 ^" W& g* F) l0 H5 c$ o
#include<stdio.h>$ I) O! |0 Y; P2 {
#include<conio.h>
( E# ~- Q2 j6 ?" h: m#include<stdarg.h># }, a5 G- ]) y, a
void tVarArg(int num,...);/*num为可变参数的个数*/
: q: O; n- B; L1 Fint main(void): c; f8 A# R3 Y/ k) A" s
{) T% l! y: y& ^. a+ t- p
clrscr();
; e* l; O4 ^( S3 [tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");) u0 M. Z0 a6 A
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
1 W! P3 F+ @: s, V% dgetch();- U/ G1 v- g- X$ v. l
return 0;
8 s/ y3 d; v- r" {+ q& \}
8 d4 w2 f7 \. \6 i2 I" q; tvoid tVarArg(int num,...)& `9 a; T. c, L7 E8 E5 l, i" V9 L
{) Y' S! H' z" Y: d
va_list argp; /*定义一个指向可变参数的变量*/5 {# u3 o( a( A) D! m% F8 ] n9 o
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/4 g$ s7 e8 y( O
while(--num>=0)
6 S* _: z" ?6 N$ a" x printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,8 \& u4 ?# w+ S* I) E7 j7 G( K
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/& z$ g7 ]' o# }" N& e( ]
va_end(argp); /*结束可变参数获取*/
4 R, V9 ?- _3 l/ [7 \+ Jreturn ;
" D) k7 y6 E& H}: L1 u% c' s/ b, t
0 t0 a% D3 O3 X( B2 N3 [4)可变参数的使用需要注意的问题
2 E& r5 V/ u) D' V+ _( M# I0 g+ j8 y) R. S2 A) x
1.每个函数的可变参数至多有一个.2 B7 h- B. d! U& z
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.% g% x: a$ b6 _. Z% m D" f8 t) ]9 j
3.可变参数的个数不确定,完全由程序约定.
* _7 G) |' C% ^3 q% T4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.% }1 r2 U% c: h/ I# e% k& \3 c
而printf()中不是实现了识别参数吗?那是因为函数
/ t b) F' D# q* v5 x8 @printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg # F+ A8 g: x. _
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
3 t6 P( A3 R8 _, F) V* k过在自己的程序里作判断来实现的.
% X' S( u; H5 O& o$ K, A& H9 t* k5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|