  
- UID
- 133
- 帖子
- 51
- 精华
- 1
- 积分
- 186
- 金币
- 55
- 威望
- 2
- 贡献
- 0

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
, P2 m' C( p: z" r它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.6 h3 c0 I; I8 {/ a
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
& P W9 I$ u. B- D定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
" b7 u: ?* J/ S$ @% W& ?$ n实际的名称与之相对应.9 w0 {" F/ }# Y- W, ?/ [
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.! h! i& {4 }5 @8 s9 P: P
然而,更多地自由,同样也加大操作上的难度. F w" d# p( S; g0 i
以下就对可变参数的几个方面作一定的介绍.1 n& {, ?4 M \( m
2 ]. g. C* R4 H% U& m% s7 g7 Q1)可变参数的存储形式.
* o- V1 n( P3 `/ i, X' @ n3 _7 L, g
9 `) q( A- m: A8 P) k2 h大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,, |& d" C: n0 ]" {* b0 t
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
, a+ {- R1 `7 P4 A% d3 m4 p在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,; i$ S, T: _! z2 a8 ]" G
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.% A' w9 y- P) ^# ^; W
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):" H* ^! A4 m T+ R( |/ p6 ^
栈区:
9 s$ S0 D5 x8 i) u/ F
; A& V) \8 x! J, {8 W|栈顶 低地址: t) V4 L1 A* u$ h. \* F
# z# b1 T, a8 W7 }1 O" [6 n9 Y0 p|第一个固定参数var1- V1 H" R( P. Z9 W$ m6 k0 ?# C W/ I
|可变参数前的第一个固定参数var20 w* ~6 M. b% ^3 n: w9 g
|可变参数的第一个参数
6 s5 g4 G- `# H5 `3 s! c' _: [|...
" S2 Q/ n* z2 k% ~" ?|可变参数的最后一个参数
4 }# L9 b0 H% _' A/ {6 S|函数的倒数第二个固定参数var3! ?, r, {2 @& d$ q; d/ t
|函数的最后一个固定参数var41 K6 v4 Y# b3 s' @
|...
; m( L& L; g$ s; l|函数的返回地址$ R5 d$ f6 |1 V7 Z m
|...6 W$ J+ k# d/ {4 o5 O
|栈底 高地址
" S% |# B8 g% _- T b- Y6 n
$ { I4 N7 \9 k4 O7 [2)使用可变参数所用到头文件和相关宏说明
: ~) I5 l- ?$ P3 b* i* ~9 Z! M% e! M& Z- T; z/ k
在此,以TC2.0编译器为参考对象来说明.
5 E$ O) R. |% a4 i+ A2 u可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
" z: l, ^7 f) V! i此文件为:9 g6 ~1 I3 c2 f4 P; J1 X8 f8 F- a T
/* stdarg.h* M {% [: a8 [/ F" j( O
, m, p% F! ~# N% |6 f3 J* v5 bDefinitions for ACCESSing parameters in functions that accept W$ z1 \4 L) d4 U
a variable number of arguments.8 m( |7 v4 R$ \. @1 E
4 _' Q9 A# \9 L3 E4 g4 E/ g! MCopyright (c) Borland International 1987,1988) k3 R9 @2 s/ Y
All Rights Reserved.5 ~$ |* d% G1 Z
*/
* j, |7 \+ A, @" h' J5 r#if __STDC__! K5 K( J, n! E" I; _
#define _Cdecl
z) k% s7 u( ~ }#else
: R" @# y4 e) d4 Y% t; v#define _Cdecl cdecl
, q0 k/ m" D2 B) Q3 g+ w9 p {#endif* ?9 }6 Q& Q' I e D# ^4 r2 x
8 q/ D& [2 K' q# Z3 x% `
#if !defined(__STDARG)
& W) x* y4 C) c) P#define __STDARG. |, g3 D+ u2 G& \$ g2 ?
* `. E7 `, [( Q, d
typedef void *va_list;2 r9 ^- ]! d( D; ]7 C- Y
4 O/ f9 P i# `0 `#define va_start(ap, parmN) (ap = ...)- k. B/ G7 Y: ~& E6 u
#define va_arg(ap, type) (*((type *)(ap))++)! S+ g& [8 G$ c" y8 w
#define va_end(ap)
9 c5 r( S! v8 q9 L#define _va_ptr (...) S1 k i; q8 Y1 k6 O( X! v
#endif; l( G! ~: Z+ X, E
9 Q5 l1 n. C1 {: o( b1 u+ V0 E6 t+ a% u- X
以上为"STDARG.H"的内容.- j+ M3 T) ~& D: v; A; V8 @6 J) c/ H
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;; J+ O9 ~2 @4 M
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list," f$ v5 w4 ~$ c$ u2 M
parmN为可变参数的前面一个固定参数.7 @5 K$ e1 `* w& |0 e9 w: z1 u
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.# N2 ^/ K0 t+ ]* i3 d# `( a
va_end(ap) 结束可变参数获取.0 k8 i4 d1 g3 `! N. V* R
! M) _) V4 ^- X; P0 n
3)可变参数的使用实例
4 {9 y! ?- n; M7 |& O7 c" A
% x2 ?/ z! M3 f8 T实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.' [5 j( v+ j+ _% O5 ?. U) x% u
% A0 [2 s" x# I8 G: p3 K) z. L
#include<stdio.h>
; U6 j' N% z0 X6 P#include<conio.h>5 q2 ]/ A3 N, l! _, n
#include<stdarg.h>
0 r: t6 N- b. \! rvoid tVarArg(int num,...);/*num为可变参数的个数*/
' f7 g9 ~ a3 T5 L) Q; `4 v- Hint main(void). C9 Y7 t* K6 [( }4 P
{. J5 U. q% z% u- K+ b
clrscr();
. q4 R* X8 L2 [3 ]% KtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");) T1 k3 o& m/ f& J
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");1 ?* c& x' i( o+ I" ~2 u* e# ]2 ^$ |8 V
getch();1 o3 d& `# ^6 `& t3 ? ~
return 0;4 N; u( i4 w# B5 h. {: ^
}
# F" i& ? o' S5 j1 s* }void tVarArg(int num,...), Q* y) j/ U- p& x
{
3 }3 |, L. |: D3 T- Tva_list argp; /*定义一个指向可变参数的变量*/" a1 n/ k# h8 x6 D& {: e
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
& s/ l. U# l# \ \* T% x. E/ wwhile(--num>=0)1 O3 T" r2 r$ s. l
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
K1 w0 l1 {$ m0 ^# ]' z 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/. ]! W( e& h/ K5 A2 ]9 n5 q9 C8 V
va_end(argp); /*结束可变参数获取*/7 V- o" }, t* p+ Y: g2 `3 f$ S
return ;
: j" T4 j7 n" j: `' I) G* b+ \}3 @ n S9 r) W
9 k4 U4 X! \! L3 Y6 H8 ~9 i$ C: i
4)可变参数的使用需要注意的问题# \# j3 I0 q9 c& ~! H
$ D8 O/ j! k) w- w2 t1.每个函数的可变参数至多有一个.0 m# U4 z* m, P* Q7 p. x
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
) d6 X F% b; x' m$ X$ b3.可变参数的个数不确定,完全由程序约定.
$ ?2 x- k3 |0 X6 n1 m0 K* J- Y4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
, d m7 \4 z; m% N8 K而printf()中不是实现了识别参数吗?那是因为函数 , |. G0 D9 U* }, T) w
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg / c/ V0 O4 m7 y3 S
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
/ c7 [" y. R" |过在自己的程序里作判断来实现的. / r8 Q7 t2 r( x* n7 w! A
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|