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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
- |. G2 z: W% n3 v0 g9 C8 k* r$ ?它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔., Z0 r0 B) ~# G% k5 c: X
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不* V- B+ E" g( o8 d
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
; ^% E- a" ]/ C实际的名称与之相对应.
% x! [3 s: ?. M/ [, \0 r8 y; G9 ^由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.5 F/ d4 E  T2 q4 P
然而,更多地自由,同样也加大操作上的难度.
9 q2 T: S- c3 t: t, l8 o  M$ V3 ?以下就对可变参数的几个方面作一定的介绍.  T6 H9 y! l# n* q* d

4 \8 `' g; m+ o8 A, e1)可变参数的存储形式.
' p5 G3 M( f/ }' B6 C( S0 o/ z# k4 O# P. O  j. `  }! ~4 k+ r0 I
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,5 B: J  G: W2 J$ d6 i1 r" P
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.  _+ v2 ~4 C9 O' ~; J, c
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
: V' O6 A5 \- l- p  h  i% w/ b这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.4 z" M" v" _9 {
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
$ U! U. |; \' c& M7 V8 w栈区:
1 T+ v: U2 \  K1 P3 {$ h4 G7 A8 U& z" R) W
|栈顶             低地址
! p+ q' l6 v  l, Q: p' P, @2 S
! s8 C# ?! a3 q+ L|第一个固定参数var1" z( i, W: ]2 V( x; y8 T- V
|可变参数前的第一个固定参数var2! ]  l- B2 O) t
|可变参数的第一个参数
( |: P) G( K9 g' T: w4 T|...
2 |3 W  W, E  h8 s% q7 F# |- _|可变参数的最后一个参数
, n+ w# W$ E- l% g" I. u- ]|函数的倒数第二个固定参数var3! n! k& I# U4 o9 p2 b" X; F+ ?7 [0 h6 i
|函数的最后一个固定参数var4  A* G2 v  W" E1 E
|...
+ g7 W+ ?2 L3 f9 z; X3 ]3 y' c|函数的返回地址
& r! T8 O. ]0 R0 R|..., ~4 g1 y% q1 B9 Z' P
|栈底    高地址
$ J6 q- t9 H/ D  n6 u/ l6 g; j/ [' N" p  f
2)使用可变参数所用到头文件和相关宏说明, R& v- z1 V$ q& i' K7 }

/ d' @2 Q' ?! P2 k2 {, e在此,以TC2.0编译器为参考对象来说明.6 a8 M: i9 U  A+ a. B
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
! X& [: Q- y4 N7 p; p此文件为:% o5 a& \' `  x# c7 G
/* stdarg.h+ g7 B& v' h6 d
* s) Q$ Y8 Q# O1 I) M
Definitions for ACCESSing parameters in functions that accept7 Y2 z! _4 M! E9 J' v+ n
a variable number of arguments.3 S8 l* O8 }: t; A3 f. Q

8 j5 K4 R% U# x0 w& GCopyright (c) Borland International 1987,1988
1 }. l. e# q- q# k; z. Z6 ZAll Rights Reserved.
& w' y3 v5 q2 `' C8 S*/
/ J: j( r3 w% ^9 f+ W#if __STDC__; X( f, i# y2 ?" W- |: J
#define _Cdecl, m- m+ T) }, A1 x' ~
#else* t+ e, K, @0 z  W) \; G
#define _Cdecl cdecl
5 o3 B. ]; w! b8 k0 m#endif
! f7 x, ~# a; s5 P  W1 V* p0 Z6 M; O$ l! M6 e0 w8 _6 x, C
#if !defined(__STDARG)/ M2 Q; F! z' F4 \& p. ~
#define __STDARG
3 M5 a8 a: N: S, d* e6 T) {
0 [7 f% b+ J, \: @' E; z2 w5 y/ |typedef void *va_list;
, \) A, r1 Q" e* v, k
: i- G' H. G; u3 V#define va_start(ap, parmN) (ap = ...)
) w4 e  j5 ~" Y- f3 D) ]#define va_arg(ap, type) (*((type *)(ap))++)3 ]; B% ~+ H. a3 c# H+ U6 C
#define va_end(ap)! u2 G, T/ r/ e, L9 t5 U0 l1 l
#define _va_ptr   (...)2 a( ~; d( A& H) T: ^' E
#endif8 x% P/ k4 U" `4 _
$ n9 ^& B, ]0 D. ^& n
以上为"STDARG.H"的内容.! ^! f* i+ J* O5 j4 Q
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;" b& Q" a3 a0 p
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,+ |  {; T* b. o1 B6 B
parmN为可变参数的前面一个固定参数.2 G# w5 N3 T7 a$ h
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.; M' {1 P6 C4 p  t1 }& {. r
va_end(ap) 结束可变参数获取.
2 E7 F; O- {1 O9 d9 e# y/ Z2 V  T2 e; g$ O# m' J0 r- t& ?0 v. G8 z
3)可变参数的使用实例
& z. W. U5 J3 ~8 W" o0 r% u, \+ t! l" d0 s: t0 l
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.9 Y& p$ a# C3 n% R; a

" T: z  G* ^) a4 L( E#include<stdio.h>( e& X* O, J! {- f9 K
#include<conio.h>+ I$ O* J, |7 |6 P
#include<stdarg.h>
5 a1 o6 {6 c8 Y- Y( hvoid tVarArg(int num,...);/*num为可变参数的个数*/. e% P3 s! T* w. _
int main(void)
+ l& S, _! x- d7 w{9 p8 w/ C3 b' s
clrscr();
# r8 r: M' P+ ]  K9 ^5 StVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");! \8 ]. z" w6 l; L3 i% ]
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");/ j; X$ d3 B6 a) G9 x7 U) d/ X& U
getch();; V* Y8 M6 Q' i
return 0;0 Q2 w( i; t: a0 R  V  p6 g6 l( K3 [
}
# b; l' B- ~9 I, O7 h) B# }void tVarArg(int num,...)  h5 i/ Q) n3 H8 q/ w  k" o! @
{
4 H5 J- ^9 R* e' h' i# x; l6 Uva_list argp;  /*定义一个指向可变参数的变量*/
  f0 P8 r# R) e0 A" V4 W, ?va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/, r; x7 I; a: s; _8 Q
while(--num>=0)
4 k4 G$ L+ X  e  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,! `. H9 h& b, H$ r* j
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
1 K5 D5 Y/ q+ q; [va_end(argp);  /*结束可变参数获取*/
' b4 @$ \* s2 w3 f$ R, r; l$ Sreturn ;
0 Y- K9 h0 z3 K% o  o1 H* |& S}8 q: V) T  D! x* s' b

* o* C0 c2 ^& ?; W, ^) y' Y$ _4)可变参数的使用需要注意的问题% L( ?1 F2 }1 H
+ ]2 p' ]" e4 q5 {: P
1.每个函数的可变参数至多有一个.! N% T" P+ L2 x5 j, B5 V  Q
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.' Y' Y' S0 q- w" p
3.可变参数的个数不确定,完全由程序约定.
, Y% I0 ~0 Z* p0 C4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.3 L% J, y8 ]4 o1 b
而printf()中不是实现了识别参数吗?那是因为函数 - e8 ]; b' y* H2 q% E
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
' v! n: O( L1 T/ s+ |的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 / R; v5 z3 d4 R1 }: n& s
过在自己的程序里作判断来实现的. ' v1 l5 T. q; u7 e+ u
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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