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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
  @6 d  U& n3 `7 x) e6 F9 G它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
1 P1 F7 _2 H8 J, H可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不9 b& R2 k" V3 }- W# v6 @0 \. {3 B
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有9 v: y) S4 X5 q
实际的名称与之相对应.& P5 A+ J0 j3 j" ?9 C8 ^/ R- T
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.) l3 M: ?4 J! G2 v: i. [$ ]) S
然而,更多地自由,同样也加大操作上的难度.
: c" N9 A+ R' e% E以下就对可变参数的几个方面作一定的介绍.) B: h& L! R" M
- A$ n7 b- R! g. z! _2 F
1)可变参数的存储形式.# F+ j9 s. B' i, s5 U6 ~- ?

3 l7 R. m6 j; h/ r  _; `大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,% [* c; d1 G6 ~
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.) e2 D5 h# A1 _) |5 n
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,, E$ f7 N; ^* C. a0 g5 c
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.' F4 H2 g1 z0 \; A6 m  r" p9 \
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):0 E& p, S/ x% ~
栈区:8 C( X' i) C' R! Z8 A$ O
* e$ g8 a) b2 L
|栈顶             低地址
9 W! r8 F4 ~. j, h1 \) u( G- \- J' t" j6 O& ^( o
|第一个固定参数var17 x- N) \- |# j' A
|可变参数前的第一个固定参数var2
  R' |& b: `, {7 H6 i/ M" M|可变参数的第一个参数0 C6 P$ ^. c6 Y3 q+ S+ `  ^2 [6 I
|...1 D8 m% }# e  \6 `5 _  G
|可变参数的最后一个参数
4 f7 p7 A) R/ l|函数的倒数第二个固定参数var3
( o4 R  R& t# b) @1 [: K|函数的最后一个固定参数var4
% ^8 e  |4 q3 N! r" c1 h! ^|...1 `  r7 @) B# o5 {4 U0 R, r# c
|函数的返回地址2 L. ~1 R* p* M* n
|...
4 a+ A% ?% H8 N, z/ v|栈底    高地址$ Z! R7 c* n; S7 B; a% ]6 w8 y
6 S7 J; K' W0 d3 T
2)使用可变参数所用到头文件和相关宏说明$ n# I* ?$ m/ t) R
1 p" b4 Y. t1 C2 }  c7 ?
在此,以TC2.0编译器为参考对象来说明.5 L1 q! Z" e) l" [
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
/ v0 K2 v5 a+ J4 g( o此文件为:
% [+ x# x4 K# C9 E) H/* stdarg.h
: C" ]' w+ X$ A8 b' g) u5 \1 R2 s8 S; W, F2 D
Definitions for ACCESSing parameters in functions that accept3 c3 T# d- [) ?5 R! S0 ~  d) m
a variable number of arguments.0 A* q$ J) A% j. N8 m4 @* s
" \7 y+ i) X* M" R
Copyright (c) Borland International 1987,19884 i/ [7 c# |0 L' p
All Rights Reserved.2 l! h6 k3 p4 K: y; e
*/' b6 d. P+ k4 k3 {& U
#if __STDC__& \" {5 q1 \& f2 \3 c
#define _Cdecl
: w" H9 h& }7 f2 y#else5 m- D, a8 q; j) Y$ o& E
#define _Cdecl cdecl/ B2 P" i  ?- V
#endif( }; H  k' `7 L+ M3 m# h

+ V0 Y+ |! V# H6 e3 z7 g1 k9 T8 U/ u#if !defined(__STDARG)8 C1 M+ \% x! S" p" G
#define __STDARG
. l# y* j8 F5 Q; Z
) z8 X  Q1 h% n- h. `; ]7 v. Htypedef void *va_list;: H6 A( A: @% o6 U! I, ]
8 z* T$ N2 H* B  l" N6 V- ^& Z
#define va_start(ap, parmN) (ap = ...)
2 p* ^8 g$ T  ^% |8 Z/ Y#define va_arg(ap, type) (*((type *)(ap))++)
" ^& A7 o' P/ i: d/ h#define va_end(ap); n2 N. o8 W/ B% F1 V. B
#define _va_ptr   (...)3 U* H9 x2 }. {- Q6 M
#endif
( ?) @. S+ N! n7 h& I' f
( |3 G2 P" q) f; I) D9 d以上为"STDARG.H"的内容.
6 \' Z; l6 V) X/ }' f该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;0 {" u! }) B* S" V, i3 S" N: {7 o' ^
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,* Z% f2 I3 e4 ]5 V  a
parmN为可变参数的前面一个固定参数.) K; U# z! p; ]& E3 N; E; b" y  _2 `
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
: ]2 e9 }8 v/ A5 t1 [. p" s1 `+ W4 bva_end(ap) 结束可变参数获取.
7 m0 R; m: c7 C7 f# K) M* E$ V
( C, o- R/ ~, t1 m3)可变参数的使用实例
& y& [- F* S8 \8 x1 x4 b( D9 l0 X: b% K- F9 y2 W. g8 r$ x9 Z7 ~
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.6 z% H# ?3 l  H5 c) o: z4 s3 [

4 Z! Y: s, d* K( r4 c$ ^8 _- A: t#include<stdio.h>
% i, O$ [  n( j% W# l#include<conio.h>5 m) g5 B4 E# |7 z$ K, L' G2 M
#include<stdarg.h>; f3 g6 k$ v4 H( i! m/ a
void tVarArg(int num,...);/*num为可变参数的个数*/
  ?: z1 _( o1 Pint main(void). y; [, u; X  d
{$ z. W& O* Z$ W0 e5 P8 H% o* \
clrscr();6 X. B) ^6 ~  ]% ~  x
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");" t: ^$ v3 l$ p6 v7 x7 q! q7 H! N
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");, y: A; Y* }, C; f
getch();% `* D" i" R- x* @  F
return 0;
( P* f# Q- H- H, r}
2 Y$ ?0 C  w4 ^" V! q- M. kvoid tVarArg(int num,...)/ I  H" j9 L) i1 R$ j5 g" k' x
{* @% l' D; O0 E+ W2 \5 v
va_list argp;  /*定义一个指向可变参数的变量*/
( E; L+ k7 J7 m3 o( I2 vva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
, I4 Z6 \7 L6 W: W, _while(--num>=0)
7 A, j8 K+ T7 ?6 e& j  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,  B- x! F" q; F( I, d& L
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
; m: r6 Q3 F* f8 I$ F' z1 S. fva_end(argp);  /*结束可变参数获取*/6 Z) H1 F) e5 t+ u0 y  k& O
return ;
- B' A0 ~3 y# K9 Y8 x( ~$ v) v}
4 A. r  I, y/ Q; |2 `* O
( J, ]# v- ]0 T4)可变参数的使用需要注意的问题
  a2 f+ R0 W9 k/ ?- ~) S8 H! Z
: m! o- k# ^0 s' Y9 b4 e; e( `1.每个函数的可变参数至多有一个.& j( U' v0 `6 X: k
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
( m, R- h; Y+ D1 x( |8 M; F% y) e  i3.可变参数的个数不确定,完全由程序约定.
9 S/ }% b# v- ?0 Y& T4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.' |/ L4 `/ c/ k+ `
而printf()中不是实现了识别参数吗?那是因为函数
$ T- F& ~7 Q/ iprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg   r, {/ B/ x% ?  W" o, _2 P* k/ R
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
1 J& I( i2 f! e% z8 p: {过在自己的程序里作判断来实现的. - ?( c9 c2 @; X
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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