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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
5 I7 K, B7 j$ f' s7 u6 E7 c% y它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
: f2 t+ c% t- ]! ^) @: a可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不, Y$ ^2 R n. O2 L, G4 r
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有0 n, E; Q: E+ D* T
实际的名称与之相对应.# O" h, S' E$ m
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
, A; C# v2 V5 H+ z然而,更多地自由,同样也加大操作上的难度.
7 P- @+ H0 x ]' N7 A0 z以下就对可变参数的几个方面作一定的介绍.
* \9 J8 H' m) L$ h6 R: F! v
5 Y! H7 w: b+ w1)可变参数的存储形式.
$ l" g& |8 @% x- N- ~4 _4 r
; S+ _' p8 B! c大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,# o" z6 a1 F9 m
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.5 y* `6 ?/ J- b8 f E) @2 K7 q* P+ A
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,0 \! P* }! w+ f6 }, |% N c* I9 S# C
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
. z/ {; }2 Z# S9 m4 x因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):! @8 f' D% X: k0 s
栈区:$ \0 c2 f% [0 V: K3 A+ S+ X
( d: {4 e' L e7 c' Y
|栈顶 低地址1 n, [2 ~# D3 B$ b
3 M0 V9 f( L3 ~# ], v2 E9 Z4 f|第一个固定参数var1
7 k+ s3 |& _- a- P$ n m# m3 Z|可变参数前的第一个固定参数var2. S( j, H1 ~. ~; {/ N
|可变参数的第一个参数
! d$ _' H( B: C$ l+ S% j) d. o1 C|...
/ F# I' E; h5 S4 P& a+ N|可变参数的最后一个参数
) x9 I9 |- @ l1 d0 R|函数的倒数第二个固定参数var3
3 \$ @9 Z( ^7 B" y* C3 e, U|函数的最后一个固定参数var4
* C& s+ v* m) v% c8 C|...; g2 g. S+ Y8 X
|函数的返回地址" O P2 u6 T0 ]" o# m8 C% P
|...2 n# ]8 J+ V4 }! j. o. y) U
|栈底 高地址
. T. ?, v Q- `$ m5 E0 S2 I- q) \+ n+ v: Q/ ]
2)使用可变参数所用到头文件和相关宏说明3 K- O/ K& T0 T- f5 f# @
1 X7 b9 D/ v7 ~8 ]5 Q4 z在此,以TC2.0编译器为参考对象来说明.
2 a8 ?, ?1 M( s可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.; k4 G+ |4 e; T6 _$ P. w {* Q! \
此文件为:
1 h2 a2 ^3 b% S/* stdarg.h4 g) ~& B/ x7 M# i5 x
; ^9 J4 T6 O2 j% F% S3 `
Definitions for ACCESSing parameters in functions that accept5 a( T X3 F5 s0 Q; W E
a variable number of arguments.
5 H5 k5 N* a/ N C& [) J8 O; E
6 W4 \' ^. J0 d3 gCopyright (c) Borland International 1987,1988
' t& e+ C% r9 U M2 b0 aAll Rights Reserved.
# G& }% v' y" @ Y8 a9 Z& i7 Q2 }*/
t$ z2 @ U( e& L7 @#if __STDC__0 n! ]; K; q5 f$ [ b- k- z
#define _Cdecl
% q% x6 m. ^, {#else" d4 w1 \4 ], ?; z- f: `2 ^
#define _Cdecl cdecl
" H# x# }" \, H* U, \; r#endif9 w* `" U K% f% g8 r9 h
e) r, Z: t) j( t# z# b+ Y! o5 X
#if !defined(__STDARG)
+ n8 m3 s1 {$ w# @2 R1 y! j$ q#define __STDARG! X6 X2 s i! ]: k% x. S
% h5 O# ^9 _. F3 Ptypedef void *va_list;
% [- Z9 f" J, x! M! D; m" w# A' |( Y* Y8 o& d
#define va_start(ap, parmN) (ap = ...); S M/ Z$ b3 {/ n* F
#define va_arg(ap, type) (*((type *)(ap))++)
# w" U9 }( t! k `+ i#define va_end(ap)
B7 m- F/ U# K/ m( ]$ V2 s3 L$ ^ C#define _va_ptr (...)
: {8 G. y/ v1 B; v#endif" a* }, b; ^1 J6 W5 C
- I' \, i2 ]' T# O, s" [. c以上为"STDARG.H"的内容.5 Y" h$ \$ Y4 a% u! t. h3 {9 i) d2 z
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;% |1 v- g& V/ Z& s
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
. X$ J% W/ J: {! r7 g4 t1 IparmN为可变参数的前面一个固定参数.
* q7 t9 {/ T% l6 f+ Y( T1 ]% h) O kva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.# c% b* x/ r' j
va_end(ap) 结束可变参数获取.! o' a* s- u4 Q: F8 N6 k& {6 @
" @" H8 \( C0 X2 r0 s! R
3)可变参数的使用实例3 r- `) p, r: e
* }# |( X) `/ }
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串., i1 [- t! p0 V6 E9 J
, }! S* j$ J0 E+ V g- Q5 ?#include<stdio.h>
( _3 R4 A& i; e+ o( Z, G#include<conio.h>
! j1 A7 ?- J9 X7 W#include<stdarg.h>
' c9 u* J0 s# G Gvoid tVarArg(int num,...);/*num为可变参数的个数*/" X) J/ Y ]% @- H3 L z3 B1 _
int main(void)
# o# y& f# Z6 a' h9 @{
+ f2 H: c0 {6 S: Y# |) Eclrscr();* R; h2 L# N! c+ v# ^9 ?- a
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
/ M9 s/ B- A+ otVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");% y( J) _7 @; _( Y7 ~
getch();2 f. E/ N/ {7 S7 G; K( y6 e
return 0;
7 a! E6 o) h- @% g$ K/ V}
2 O+ G- w. q8 i5 L5 }void tVarArg(int num,...)) A: J, K4 ]6 d9 w0 [ _% B
{
# |" |+ m' c. l% [va_list argp; /*定义一个指向可变参数的变量*/8 `" R! X s: X! I& t9 }
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
5 n2 ~( x4 \' Gwhile(--num>=0)
7 Q5 D: ]6 F5 p# U& x" S printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
" N9 w. ^- @) F& r a" b 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
% O8 i" e4 }* n; u* vva_end(argp); /*结束可变参数获取*/
: |9 z0 f; A) \. `return ;- B; { H8 C; k$ b' a: g
}; Z; ^7 ~' ~2 ]- X* k, V5 q! X7 B! N9 F
, f# j3 b7 _1 r, W$ E( L4)可变参数的使用需要注意的问题! ~/ a4 C. c* @" L3 b* `% O7 d; D1 h2 u4 o
% W6 D; G. m \' E& W: G+ j
1.每个函数的可变参数至多有一个.
* P5 }6 t7 [4 R: `. G% J, n& C$ n2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.' W: b) \/ u3 j$ v
3.可变参数的个数不确定,完全由程序约定.* X1 E! {& u- T/ s& b
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
9 r7 [2 m1 p6 Y# O: p9 A: X而printf()中不是实现了识别参数吗?那是因为函数
* O3 V: r2 a1 @" X5 Qprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
. ]* k) l W1 s; Q" a' s/ x的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
9 @6 r& C$ n5 j2 x! b过在自己的程序里作判断来实现的. 3 j7 p. O- @# p6 C; Q# ~) p; O% ~. ]
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|