获得本站免费赞助空间请点这里
返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.
' c' t7 F$ G$ F$ {. L# m它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.3 N: c9 f! O3 M' i
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不  |  k* {1 v4 g% w: A
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有& F2 _5 l0 T+ ?! i* I! D
实际的名称与之相对应.1 A5 J$ K9 I' d2 ~
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间./ Y: A2 k  S! Q
然而,更多地自由,同样也加大操作上的难度.6 x3 Z# |4 I; Y) a% y, q. _" i, F- A- E
以下就对可变参数的几个方面作一定的介绍.; M6 T& J7 d3 ^8 _, \7 g5 u' y
$ N0 B1 O; K) a) \
1)可变参数的存储形式.
- H$ _! o1 X" W7 t1 I9 O) G2 A
2 k) G" p: v9 @. Z5 r大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,) K: c/ Z2 ~, l+ X
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
. B* Q. s/ s# p. R: @) e  h" ^在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
& ]- Y; e3 J3 a6 [% A) D) _! a这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
( g2 m5 U( S" V因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
6 P9 R& S0 p2 b3 f- u栈区:
; S4 r- S  i5 c$ a) v% K. {3 @/ I% v9 Y
|栈顶             低地址6 F& i& J5 P# f3 c: J& o# z: L
& V+ Z" t4 X( k% i: t2 M
|第一个固定参数var1
: C- ~% x6 s, p|可变参数前的第一个固定参数var2
3 {2 T  e3 R+ T|可变参数的第一个参数7 @/ ]$ g5 U. R8 p
|...
2 b6 Y* V/ X, @$ U|可变参数的最后一个参数
8 ~% B2 G8 j+ L' p7 d) R|函数的倒数第二个固定参数var3: m, J6 {2 g! v
|函数的最后一个固定参数var42 a) y8 v- c$ N/ X. ?+ n0 N7 p
|...  _4 }; S# B* A! T! c1 |  l
|函数的返回地址
" C" u2 M& \4 p|...
5 o# r# m. ?: b) u9 Y|栈底    高地址
- O+ |' _) J# }
7 T: B, h+ I, j3 o1 A2)使用可变参数所用到头文件和相关宏说明$ J6 F* d9 {5 b1 S# \

3 b' W- V- D5 U- N+ w, @8 s& Y在此,以TC2.0编译器为参考对象来说明.
$ K* m/ a5 ?( f可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.! f. f* t- ?3 z9 U' b0 A
此文件为:
3 Y+ O, }! }& o, C/* stdarg.h
1 K' n) m. V: k. _9 a- G) W4 F8 G! B/ m: A, p+ e
Definitions for ACCESSing parameters in functions that accept
/ [7 j! Q  S+ da variable number of arguments./ L. U( R$ k6 `2 n
+ i0 ^+ r, U1 x( K( ~. G, M& X+ @
Copyright (c) Borland International 1987,1988
! X9 k5 [8 w' h) z" S4 YAll Rights Reserved.
/ r8 k( i% [( f0 X, Z3 }% u*/  q" @0 p; ]: j. o. E5 E
#if __STDC__
8 |, J8 p; B! t; }- F: L#define _Cdecl
/ b" W2 G1 z% j4 z9 O#else
- N- M  g% [  N1 T6 u4 Q#define _Cdecl cdecl
4 C9 t# L4 W2 h4 @8 S7 K#endif
, ]) P0 M6 C+ ]$ Y9 \1 X% q/ S/ ~8 p( C
#if !defined(__STDARG)
" X/ P: l0 p( k  @1 U+ v#define __STDARG! N4 u8 w# r! k$ r/ f( y
: x2 s% R& B' U
typedef void *va_list;
# g; U' z7 j% U9 S) D9 t; L  Y. B( A! p; z# _8 A6 n" j9 U
#define va_start(ap, parmN) (ap = ...)$ G: D6 d- ?1 v5 R: f' @
#define va_arg(ap, type) (*((type *)(ap))++)
3 f% j7 Z) R! s- W' U0 w#define va_end(ap)* X+ U* c( E: N8 i9 t
#define _va_ptr   (...)
4 ]" s0 N7 ~: h3 W6 x4 Z#endif) ]' |5 Q& B9 e# Z2 A% Z' G

2 \# W1 j% \" x& I) Q; ~以上为"STDARG.H"的内容.* [- J3 J8 T* o- l) X7 ?% i: c# X1 G4 e
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;) o6 _3 p4 x! f: R
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,3 F" ~' _  h# R/ H. E. k5 |
parmN为可变参数的前面一个固定参数.
2 a3 {3 h0 K/ D9 n8 D' rva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
# ~$ F; Q2 _1 v; Dva_end(ap) 结束可变参数获取.% D; X. U0 ]4 V% ^
/ S3 z3 p& X$ M" r! A+ \
3)可变参数的使用实例+ p: `0 h4 D: P' a3 m

$ c, H/ q( l  U4 q: V& y- i: \实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
! V$ @  B, a2 j- k; T% U* B. g
7 R; a& i+ @; T4 L/ l! B' M5 S#include<stdio.h>5 i- y; C  K1 ]
#include<conio.h>, O" T+ X! h' H$ L2 d! p: G- ?
#include<stdarg.h>
( {2 e! ^$ Y* w3 _! Evoid tVarArg(int num,...);/*num为可变参数的个数*// k+ W& d" }& w. P
int main(void)! B. ?! ^' Y6 M
{
3 `$ [  k% \! j% p  [clrscr();8 N5 b/ e* g1 s( n
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");3 l; I+ i5 ?! U( A5 x3 F
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
, ]2 X: q) I4 g: egetch();% G9 [" M0 o2 |: a; A( L$ k
return 0;
: n# L! \- g/ Z  J3 V- t; o}
' r4 G& k4 Z2 l" H$ W5 @: Nvoid tVarArg(int num,...)
: N7 M3 ]3 M8 {' J1 A{
4 k9 L: l! R# N1 a2 Vva_list argp;  /*定义一个指向可变参数的变量*/
8 g) V# c) L6 u( @- y" Sva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
& J4 a; w' x  E# p5 ywhile(--num>=0)
+ Y9 N: j( _+ N) E8 g  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
' Z/ y2 A2 [! z; G* ?    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
9 j/ R) a# K6 a! Z/ J  bva_end(argp);  /*结束可变参数获取*/
+ a  R* m, f( q" X$ C# mreturn ;, j7 O0 b2 g& M6 u& m, Y7 x" J9 _
}% c( a  ?5 A/ O% }5 e+ o1 e
5 l% E, m& Z2 B& r
4)可变参数的使用需要注意的问题- E: u$ n/ z0 S. y; }: w% I+ G

7 m* s; W4 j* \8 k1 E, `, A1.每个函数的可变参数至多有一个.
# R1 M+ E6 m) E  Z& Q2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
( L3 Y+ ]- n# Y' v6 }, ^3.可变参数的个数不确定,完全由程序约定.
0 ~7 G; g; a2 u) s4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
" {$ D) M" Y6 Q7 N而printf()中不是实现了识别参数吗?那是因为函数 , z: q! A2 Q( Z! G4 C7 D+ X& H' h
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 8 J- u8 w7 H3 q4 X1 c2 m  t
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 5 S$ s: b4 C$ C/ q
过在自己的程序里作判断来实现的.
' f* l! j/ t7 E, z5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

返回列表
【捌玖网络】已经运行: