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

函数的可变参数详谈

可变参数的英文表示为:variable argument./ E" L  Y  z8 r) ?
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
" C4 O% M9 |: i8 \可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
( k8 Q: P  n5 F8 m) ?5 v* e* b定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有9 ~( a% O- N& B6 B
实际的名称与之相对应.
! q3 B4 G  w% H/ O, Z5 E" L5 P由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.  j# R! f* U# H6 u. l# A. ?1 w3 S! A8 ]
然而,更多地自由,同样也加大操作上的难度.* X- F6 I& t0 x% @
以下就对可变参数的几个方面作一定的介绍.
; Z* [/ B- p% |& r8 R: h1 l( }) T/ }: q+ h# g6 H1 u
1)可变参数的存储形式.# w1 d. p4 l- ?! s7 x5 B1 B

! p/ |8 a- Z8 e5 x) h大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
/ A: L9 Y2 d2 I# ]4 V3 h9 t存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
* {3 i2 B7 I5 z7 X+ i在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
8 z" K% p; k- ~这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
: Y$ e) |+ ]& e+ I因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):" w# J1 M: s( u3 X: }; q6 {
栈区:5 @1 u- X$ Y% f3 H
1 ~4 O) @$ \# j9 Z, c6 a, W, V
|栈顶             低地址
' T2 a3 w5 W( N% g
" s+ \- `1 ?- ]6 c|第一个固定参数var1
9 D' `2 z# U" |, A/ ]2 N0 f% B|可变参数前的第一个固定参数var2
/ B2 E! `2 t0 w|可变参数的第一个参数
# K  q. N# G$ D  J$ @) C|...1 q* h6 H7 w' G0 n1 V) s* a
|可变参数的最后一个参数6 B2 [* ^4 E+ [9 {4 W
|函数的倒数第二个固定参数var3  a: p& f( Y0 `' }3 k; v* j
|函数的最后一个固定参数var4$ n9 [$ G" c+ A8 [" J& e
|...
8 C1 |* e" z: p2 }# M9 K|函数的返回地址
1 a6 W* }+ c3 @/ ?2 T|...
$ h. g. \3 R% O2 h|栈底    高地址8 O8 s$ _6 P5 [5 Y3 C; T7 b6 e( q. c
8 C2 C2 T) ~7 |, ~7 Z/ w
2)使用可变参数所用到头文件和相关宏说明, G& d9 c# J+ o) ]
1 `, O- g! j: m" f* N7 X5 j. M
在此,以TC2.0编译器为参考对象来说明.
( b: C  \/ J9 S; U: Y( z8 U) d可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.5 Z: L8 p, W" I4 R8 w
此文件为:7 R$ Z" Q+ w% G/ E  r
/* stdarg.h4 ?3 N# f5 z" t
- @, ?2 _# R% w! ~
Definitions for ACCESSing parameters in functions that accept# I# Y5 ]$ O% X, V
a variable number of arguments.
0 {: C% D7 S, ~
* V& x* c7 P, vCopyright (c) Borland International 1987,1988  Z; T# k) n5 [5 y
All Rights Reserved.. S! i* p$ x0 V8 t: G( k# a
*/1 b6 Q, c/ U7 m0 M; U! M
#if __STDC__
2 e/ [- o7 w$ ^2 b& `#define _Cdecl: k/ b( T" [- k1 h( ~! t
#else
0 D0 }, ^# j( k- m#define _Cdecl cdecl+ i7 i3 Z7 B9 @
#endif
" Z5 Z% F' v  W3 A& z* j; [5 }
0 i8 E* ^( b4 @7 ^7 y' b3 Z: r#if !defined(__STDARG)9 y" N) v4 o& J/ F) M
#define __STDARG9 w9 ?& j3 ^( O  U7 m% I$ G5 d
& f. x  _2 e0 Y, O" L
typedef void *va_list;
6 B$ |5 u6 ?; V% `: F, j% n# r* M9 B' J- s6 {8 n* [( E& f5 F) w0 k" c
#define va_start(ap, parmN) (ap = ...)
4 C" }/ ]( h+ h) v1 Z  b7 r#define va_arg(ap, type) (*((type *)(ap))++). \* h) b) B9 S0 x. y
#define va_end(ap)
' L) @& _/ a0 a* V& {* }: c' I#define _va_ptr   (...)& R( E8 v/ Y$ C/ m# R- T, Z5 M
#endif
; l4 d; k7 T0 E5 p$ A# x* {, T5 Q% C2 ]6 z6 r* T: e
以上为"STDARG.H"的内容.  d! ]  Q/ D' b' w0 o
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;' j6 [- x3 _) K  J3 e. G5 u
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
& n1 G+ w' j( A! n# a" L- h* AparmN为可变参数的前面一个固定参数.
" [% n! `* ~* l- R: s+ pva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
' a3 n, e- ?8 t$ c  nva_end(ap) 结束可变参数获取.6 f0 x" x6 a  ^" x( C

: p; L1 \: y, b: |% }8 j3)可变参数的使用实例
- F1 s  P; W$ u, h  Y7 Q+ ~8 u5 J4 \
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
: ~) H! y: f* R1 s6 d
& W6 q6 Q! H; w" T2 A' C3 c) j4 l#include<stdio.h>
* ^! I+ u, F6 S4 z( K" ~#include<conio.h>
( x$ u+ g6 Z% R9 I& u9 Y7 `#include<stdarg.h>& N. E% u+ n2 |' `
void tVarArg(int num,...);/*num为可变参数的个数*/
( n1 Z8 h8 u. y0 l2 @+ r/ Dint main(void)( ^3 _+ i" q6 s& c; ~
{6 O3 o- k* j% B3 S; }; e+ |5 X
clrscr();5 M; I, v2 h' k
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
0 }8 Z! e, K) ltVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");1 S  r; F3 d/ D) s1 b1 @
getch();& ~5 i7 t1 D2 B. H* \0 z: D, _% Z  o
return 0;
1 ?& S. q& ?) K+ C/ Q}
' j/ C% B8 t8 c6 N$ L* M5 c/ cvoid tVarArg(int num,...)
6 M  h) L, S7 O  H$ I% Z  U6 w{
6 Z9 p' M9 _9 Y/ u" ~8 [& Nva_list argp;  /*定义一个指向可变参数的变量*/
3 L3 f4 Y8 c9 R$ J3 F3 O+ [va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
& @! r% `2 n: I" `9 twhile(--num>=0)- m& L* M8 ^7 O7 K  j; ~
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,0 A% L7 x( C$ F2 Y0 `/ ~
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
. t: l( J' i5 O8 r- [va_end(argp);  /*结束可变参数获取*/
$ E& J  g1 e% x0 i' _# mreturn ;
0 R! L$ k7 p3 S  I6 C}3 B, a& n* n7 v$ D6 o

4 Z. h. w4 }6 K' g! f6 }4)可变参数的使用需要注意的问题6 j, t4 i: s1 `+ k; s9 D1 U1 `$ a
' D) n; A$ @! G/ ]
1.每个函数的可变参数至多有一个.
% H/ s' i+ P& Q2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数., q8 y6 R( _) i/ U) A; @3 v
3.可变参数的个数不确定,完全由程序约定.  s" r+ ^3 B- ]% `$ [2 q2 j
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.+ [) l- k5 s2 j- u3 B# F
而printf()中不是实现了识别参数吗?那是因为函数 $ f5 {* w3 M1 J5 C, f) s  o" @
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 4 V% m% m( _0 w$ g! |: M
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 $ |- [, r. n( k; [; l) w, e0 x
过在自己的程序里作判断来实现的.
% K7 E- }4 |3 S" {5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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