返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.
( a2 H9 ?  r0 T/ u) ^6 S2 n它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
4 @% S. _+ [, d0 N( e/ K" j5 h可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不8 b! L! c# U" _  U! M% }
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有# ~) m& b! Q, F# D$ m- P; Z7 o$ [
实际的名称与之相对应.* i3 Z6 F4 N6 \, O! I4 l
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.& j. U4 t( Y7 H0 A
然而,更多地自由,同样也加大操作上的难度." W6 i6 s7 T8 W5 a* w
以下就对可变参数的几个方面作一定的介绍.4 a: Y. |' m8 [* l
0 b7 Y2 H* B  a4 j" M
1)可变参数的存储形式.. T6 X  v2 I0 p  T9 Z5 t

& ^! a: H6 ~8 m# ?大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,$ r' N& m! ?* x+ |# I# I
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.: L, \# p! W- y* M' B
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
! R' l5 x5 q3 \$ h- z' ]* r  q$ y; s这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.6 E) r3 Y! Y6 ?4 V' Y
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):1 o$ M1 b' h: Z4 T
栈区:% V2 a- b; @# X

" t: r) E, U4 A, ?! S* j|栈顶             低地址6 \* r1 n6 ?1 K6 S& c  p) r

2 g/ c! B/ _& N1 H  I  H|第一个固定参数var1" ^: a6 |! f0 ]- J4 R( q
|可变参数前的第一个固定参数var2; n# U& X3 v$ N1 @6 o* c
|可变参数的第一个参数
$ a0 ]+ W; x! k6 B; w/ v' G|...
. v5 w9 ^3 O4 _8 }* ?|可变参数的最后一个参数
' m0 k' F+ X3 h; W: l|函数的倒数第二个固定参数var3
% \( ]* N/ }& w|函数的最后一个固定参数var4
" {1 E$ T, ]% F! r  y5 V$ }. f& Q|...
/ L# {% O2 M. C. D2 n# V|函数的返回地址7 ^  Q7 G2 R1 D5 g' j7 q! j
|...4 S& J  o5 h* ?+ j9 S" n. p* v
|栈底    高地址; \! \2 b5 L0 x; E; w% O
. s- r2 @7 {  l
2)使用可变参数所用到头文件和相关宏说明" `9 z# a/ a& }/ \9 e  t
# v+ T: r8 f) U
在此,以TC2.0编译器为参考对象来说明.
9 `, e1 s) r: J4 a1 L, y& a  H可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中., I9 d0 |) A2 |( O$ n5 |
此文件为:
: `- x, i* D/ ]7 f1 x: ?/* stdarg.h
" m, b( ^8 R, Q: J1 t. {4 c  ~; G; F5 p) \9 b
Definitions for ACCESSing parameters in functions that accept8 {$ o4 C1 B# O  s/ m6 Z3 Q
a variable number of arguments.& ?7 ~+ f8 p# G/ X/ {

& h+ q# g$ U2 c; |2 h& b/ gCopyright (c) Borland International 1987,1988  |5 I5 E8 O( u' o1 @1 P7 g7 i& |) P
All Rights Reserved.
. [0 P! L. _" m" I. {*/- D+ r% E" L: K: l1 D
#if __STDC__& S- V- x' P! W: {7 R2 \
#define _Cdecl/ x# L# l- U+ A" ~
#else
( u( G" z' `8 D5 l3 ^#define _Cdecl cdecl
7 o5 m% S& u; ]$ N* F' o5 O% w#endif
+ p9 a' u4 I* f  w1 \! |2 l
+ a+ b$ f: D  b5 [1 i+ C; z. o#if !defined(__STDARG). Q: C  A# y3 q6 @
#define __STDARG
2 F* g$ [  c6 ?$ G
& u# q5 t7 k: stypedef void *va_list;+ D, |0 X5 J% `3 v/ Q# @3 H% c
& o8 G3 w! {* H0 J& n
#define va_start(ap, parmN) (ap = ...)8 `9 n! f! V. b* |) e
#define va_arg(ap, type) (*((type *)(ap))++)0 N7 ~; e5 W) g9 Y3 q/ k% V
#define va_end(ap)
* z  k: q, ?; ~#define _va_ptr   (...)/ |5 j: D' O  T0 k
#endif: N4 c3 w, w) m, p

' E# F9 X( x9 a7 H1 m- o/ R以上为"STDARG.H"的内容.
! H: s9 H, C+ N/ e( K9 O该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
9 {& p3 x+ d, W0 E" A; fva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
1 Z  _1 X# i& F1 ~' o3 gparmN为可变参数的前面一个固定参数.
- a8 `1 |  W) A3 nva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.8 c* k4 s. d9 y. J$ }
va_end(ap) 结束可变参数获取.; h" n. l6 w  P
6 ~& H. D( q) y# r2 i0 l; j
3)可变参数的使用实例
& H' [+ o6 |; J* c- z8 Z& ^; ]7 M& r4 }# s
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
- b! S# b; b* K' ]) c% O- A  N7 B) E7 ]7 \1 I: Q& y. x
#include<stdio.h>
' u% q0 k3 N! Q0 }#include<conio.h>
" z, M1 J" _. Y$ O4 c) C/ `' ~2 g#include<stdarg.h>
) T# A- o* e8 F$ ~0 C8 Wvoid tVarArg(int num,...);/*num为可变参数的个数*/& b6 E$ y4 u5 ?, n; r, E( X0 q/ v
int main(void). G7 f! V8 |1 K1 r. m$ \5 ~/ @
{6 j* r" g' i/ P2 c, a
clrscr();
7 I# V" y4 D% W( z! j# H) U0 TtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
, x8 m  o  m' [8 itVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");3 I% o) P8 {% j
getch();
  H1 `4 K5 j% ?* S' B5 Qreturn 0;% K" v3 ~) `' ~# T( N7 N
}' [/ M* l/ ]( _& |/ I: E
void tVarArg(int num,...)0 I8 B% V9 o. z
{2 G: s8 `2 }  Z- m4 i6 H2 u
va_list argp;  /*定义一个指向可变参数的变量*/; R  `' u9 H, f( x, J
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
6 Q- E/ s) T+ A- U6 \" awhile(--num>=0). _1 {. l( l" e1 g/ a
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,$ E) X4 |: ~; H; `) A$ K& f0 z
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/) m' m3 U- |9 F
va_end(argp);  /*结束可变参数获取*/
7 [/ |$ m- \6 j# freturn ;
; L# N" w3 }- P! l" X& V' |}) Z( S- u7 |; p( e" h
8 E! s: I9 B3 e% W% T$ \
4)可变参数的使用需要注意的问题4 V, x4 g$ B5 h! |* m2 O
, p3 S6 w' ?7 J; f+ A
1.每个函数的可变参数至多有一个.( m5 u6 o8 u( v" c& a% w6 A* m4 s
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.3 a  A" g( H  w8 W7 S# Z6 c
3.可变参数的个数不确定,完全由程序约定.+ a: l7 m- h% @# C; ]  O
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
1 _1 P& u1 X' J, i而printf()中不是实现了识别参数吗?那是因为函数
/ t# V# S4 K7 \# Tprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg # s: e) B% ^5 W4 {
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 ; }, Y& B% J( }7 s
过在自己的程序里作判断来实现的.
. m% K5 _2 T; Q" e5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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