返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.
1 p, \- X, T; x它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
" N4 |- g2 W2 M+ n# N' e可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不3 n6 r  ^! |6 |
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有! G7 u2 w7 K6 W& K# p5 @; ?( g
实际的名称与之相对应.( f7 c8 _: q; x7 @2 F8 U+ W6 n
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.. q3 J; ?; a2 j, A9 N
然而,更多地自由,同样也加大操作上的难度.5 p( l) }& p1 Z  A; \
以下就对可变参数的几个方面作一定的介绍.- F* A/ T. f9 [
1 p: e% ]& u( @! D, K
1)可变参数的存储形式.3 w3 ^+ ?+ B4 [9 \. B% H% G: |
1 K9 s# }# h3 v$ d2 V3 ^4 ^
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,3 N. |9 }- t- G- q" b) E* k
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
# X$ e# }* Q6 ~# ?在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
, P5 [! ?, |4 ^$ p$ Z+ n2 w这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
! r2 j1 r6 C' _+ M6 S8 o. V因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):" G# E- x2 b6 K
栈区:
4 Y  I# e" J# ~' o: @1 K  d& a' {  k  o9 V0 O" O
|栈顶             低地址
; u) \3 k' S2 m0 c" O$ J/ c1 `8 `  z* b2 u# ~# o4 S9 [
|第一个固定参数var1
# b3 x: {9 B8 M! w1 U; X* R! ]! h|可变参数前的第一个固定参数var2( ?  e) x( {2 q! i8 s" i% \
|可变参数的第一个参数$ [$ g4 N0 |+ B# m) a3 K# S! I2 p
|...
* g1 \& S& Z& U9 J5 x8 U* K; X|可变参数的最后一个参数
2 ?, O8 L5 _4 I) J# y|函数的倒数第二个固定参数var30 A6 s5 H( p. Z# S+ V/ B
|函数的最后一个固定参数var41 V! G2 i9 [0 d6 ~, _0 w, C8 {" e
|...
. ^) Z- z# ^* `% S4 m( t$ @|函数的返回地址- N4 [7 N$ t( F0 w, G
|...
2 |5 R1 ^& A, e- A5 E6 s5 [, q6 U|栈底    高地址
3 T+ s& F4 d* R/ r- u; l) H
. k/ S, }" [. Y& V2 x! ?) n! }2)使用可变参数所用到头文件和相关宏说明7 J0 E+ E0 a1 t3 g; W" F. Q

0 `/ `) G* f) K0 t1 i- t1 x+ s( e' Z7 \在此,以TC2.0编译器为参考对象来说明.
, M0 w! o( u2 ?! E8 e可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
* f$ w' _% M# j6 {  i$ Q此文件为:
! _' v6 l. J( ], X: e/* stdarg.h
9 v0 c  [$ G% p$ }
! L# U4 p7 @, c3 s" PDefinitions for ACCESSing parameters in functions that accept# j1 j. C- J, R: P& R
a variable number of arguments." g4 j' d, g/ ^

0 P. K8 X- o" S& o( b/ dCopyright (c) Borland International 1987,1988
5 `8 h" M2 z5 _0 k( B+ O1 x! Q" DAll Rights Reserved.( ^" S4 b* E$ `8 A/ l4 f$ `$ r
*/
4 s; ^) \" `* q3 o1 H+ ?#if __STDC__
$ a/ M& d) y! t) Y#define _Cdecl
8 a+ I% t' t8 N# S/ |/ L#else
. \* G" T7 R* s1 r3 n#define _Cdecl cdecl
; C+ g8 x6 t2 k7 X#endif# l5 B$ O6 k* A5 M) u, v
3 ^6 v1 d9 R" ~1 |8 z) s
#if !defined(__STDARG)
6 S8 K1 d7 R7 M- u#define __STDARG' I& }  L3 h" |0 L- Z* |3 U

' }' z: V( I! s  L: Y' }( ftypedef void *va_list;
- ^, O  p+ ^+ f# W4 I/ `  W& Z
8 l( m$ ^4 H7 q' p0 O3 A" [; t1 g3 B5 d#define va_start(ap, parmN) (ap = ...)
) M, Z+ e5 }* b/ A# K#define va_arg(ap, type) (*((type *)(ap))++)
" N; G- O* {3 |; [4 ~$ f#define va_end(ap)4 L) v# _5 K; e& k2 C* ^- v
#define _va_ptr   (...)
1 d( S2 K' A' z* n# ^* `3 k# }) B#endif& `' @9 }* }9 A5 y! Y8 F  P# ]
& I- L+ r3 ^& y' d8 E
以上为"STDARG.H"的内容.+ ^, p7 B/ V* w% H( a
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;& j) H  X! Y* a$ l- l- s
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
0 v) \1 z4 y, W, z+ j  ]parmN为可变参数的前面一个固定参数.9 q" ^* G$ p- x& ?; v. f
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
* C& R, |. B9 @- N3 V% gva_end(ap) 结束可变参数获取., C  B3 W- K1 H1 f! s( i

1 [/ p" A' [$ G3)可变参数的使用实例
" l& \1 Y! t6 e' s  C* m) y
# B; T. y2 Y; t- x* X实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.7 f; \+ {$ Y1 p1 G! O
  F3 t) y0 T, B( T2 i: h- P+ i
#include<stdio.h>
! p8 v2 V6 Y( s#include<conio.h>1 l9 ?# S9 F& A* @5 H' ~
#include<stdarg.h>4 e# Q, |1 ~- o9 ]8 I: i
void tVarArg(int num,...);/*num为可变参数的个数*/
: |  \# ?% G. P! @int main(void)4 {) n- ]3 y2 c6 [, J
{! n# ^" _1 Q( {5 r' R
clrscr();
( h7 {7 }1 B4 o" atVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");( \( S3 M1 P6 [( t( ?1 \' L$ {% ~
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");$ {$ a. N% T. b) j& k7 M" `. }4 U
getch();' m0 d# u9 H( U& ]- n, ~
return 0;4 ?# C; h! l( G! v3 I! c
}
5 S* J1 B; T. o0 F2 J! cvoid tVarArg(int num,...)
# T" K* ^8 z( L5 e: T0 I{7 W6 ]) ~, |8 Q' H' u! j7 w
va_list argp;  /*定义一个指向可变参数的变量*/
" ~$ l) B% \; |  v9 }0 lva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/: J- T( N4 J9 O/ U
while(--num>=0)
! L0 \8 ?2 l* I8 k, s: p0 _  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
, @; e2 t6 f9 p, A" |3 ]( l3 T    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
7 h# R+ z4 X  e/ z% |va_end(argp);  /*结束可变参数获取*/
% z( q  h$ ^- M8 O7 e, a, treturn ;& i$ |5 A- U  J) e, s6 O. g
}
1 `6 \( `3 a9 r+ L% ~# s7 u$ |/ p9 U& B3 ^) C; p8 I& W" O2 ^. x7 A
4)可变参数的使用需要注意的问题
$ _! v* D6 t) M9 C) z. |; w" B( d% e" B7 c( G% p8 K3 ?% w
1.每个函数的可变参数至多有一个.
# A) K, \; J" b/ L& e, k2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
* Z! ~. e- _" u( W% O& y- V3.可变参数的个数不确定,完全由程序约定.2 A1 E4 E: l% n/ p6 u$ D
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
6 V/ B& S5 `- t/ o3 r, _5 p而printf()中不是实现了识别参数吗?那是因为函数
+ C9 H" `- J1 g/ Qprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg " ~+ B2 p- W% {4 x3 }) j
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 - V5 w- h$ l) b2 f& g4 ^
过在自己的程序里作判断来实现的.
" f; f- U. c' q5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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