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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
1 S* Q& J7 X6 L" P) X" A2 ?它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔., d6 _2 W0 I; }; T& n7 I
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不8 Z" L0 | V& | n) S
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有5 C! z& t7 ]) M. x+ q0 {0 I
实际的名称与之相对应.) j: m6 Z& x* P, R
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
) v6 {$ b' ^9 g# R然而,更多地自由,同样也加大操作上的难度." W) I S1 c7 _% @3 t# Q; V
以下就对可变参数的几个方面作一定的介绍.
. }8 {7 I3 E& J+ N; W4 g4 m8 a1 ^* V; q$ f& D, G; D
1)可变参数的存储形式.
! ~! z* x# K9 }, w: B1 Y e9 M- {- N+ @9 {; Y
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
/ T- G# R6 F: m' C" A+ y$ _, F; U存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
9 N' ?# e, P! Y/ B在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,0 ~3 D' Q! R# j' z7 w
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
5 s8 \% ]4 r2 [因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):4 S6 v# D9 V( g4 j2 {' G2 ^. V/ y! C! j
栈区:
6 z" y, H1 H3 n, [) g5 I& r. L3 v! k6 |5 B, r6 y3 N5 |! p9 N- X
|栈顶 低地址
9 i2 O7 W; U4 R4 {5 J7 B1 A$ Q
6 p5 o3 q- }$ v0 l z ~! }3 g|第一个固定参数var17 G* w( q3 r2 r8 L, G
|可变参数前的第一个固定参数var2
% s5 z! U* `# s/ K/ t|可变参数的第一个参数
1 `. `- B5 |: Y3 a, Q|...
# ?, r" \! Q0 i1 U5 x% `|可变参数的最后一个参数2 i6 I4 t6 i" l6 ^
|函数的倒数第二个固定参数var3
5 |# N8 s4 m& L' A/ U% @" T|函数的最后一个固定参数var4/ e6 T& u7 ]/ k* A
|...2 Y: j {& K2 C- a, Z; O" k
|函数的返回地址' Q4 g9 ?' u0 Q
|...
) g" x) y( P& k, S7 {. v; _: Y|栈底 高地址. c* J/ t+ A5 W: X) O* G) m
: u% w2 T9 T* c% r6 h. @: @
2)使用可变参数所用到头文件和相关宏说明3 e# T4 K' A' ]4 z. o3 t
5 G' v! E: {& e在此,以TC2.0编译器为参考对象来说明.
. Y, \$ N- q0 X' G可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中., l% H4 i7 s& \" |. Y8 l
此文件为: s, l* L: x% I5 c$ x
/* stdarg.h
+ w# b: G h% N; f6 d3 _. }' K$ @% t9 K: s M- z ]( O, r! s
Definitions for ACCESSing parameters in functions that accept) Y. F2 I6 \8 |$ ~# E
a variable number of arguments.
1 z# r! w! m) W% l1 [& N
! r+ C# \* {: ~! ACopyright (c) Borland International 1987,19885 a3 D( ]9 ` e5 I
All Rights Reserved.4 B9 X3 e) H- r2 t- t$ I; m' L J
*/
4 w$ t7 E( u2 a; `#if __STDC__
6 J, l" C% K, q( h! W3 F#define _Cdecl! r( h+ v0 v0 D( i$ y
#else
0 j% C( q; B3 e3 y* \% z2 Y2 x8 A#define _Cdecl cdecl
0 d) v& `1 m3 l3 X#endif/ @ z \0 w6 N8 j- H5 P/ D: r: ~
5 j: Q) W4 r; X#if !defined(__STDARG)& T6 V" i/ w$ Z0 W+ @2 y5 \
#define __STDARG
+ z; `6 t; j; ]& Y' C% i) c! y5 Y9 l) A8 ^" t
typedef void *va_list;, |0 g: t, x1 T* c! h* L, T( G. H
3 }/ x! X: }. d6 \; z. g#define va_start(ap, parmN) (ap = ...)8 L9 y* q& B2 E `+ R+ E: d
#define va_arg(ap, type) (*((type *)(ap))++)5 l: g/ l9 k9 ]1 n
#define va_end(ap)9 Y) l4 _- x& D; T4 H1 p, w; j6 ?+ K( `
#define _va_ptr (...)
$ [1 @( v6 M: k#endif( n) w4 s% Y0 I' `# e/ e+ y
8 T/ D( n0 G7 H! P Y以上为"STDARG.H"的内容.5 d: Q, ^8 w) U+ @, Y3 Y9 N0 h5 E
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;% z: q1 [5 x* h
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
8 \1 Y0 r' Q& o' k. _parmN为可变参数的前面一个固定参数.* L5 i1 {9 Q. M- R& S
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.& O: f# G- d/ \: y7 W7 r
va_end(ap) 结束可变参数获取.
`5 |6 @' T. M$ i8 R) t6 }& c/ ~& d" K, w+ l! v5 N1 i |
3)可变参数的使用实例' n' w# C) l: @- V: [4 I
/ i8 E' D9 a' E) G实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.5 W/ B( X5 z' p6 [4 `, Q
* a& n: U3 C% h, Y' a: R1 \#include<stdio.h>
( Z! P1 W; y6 |#include<conio.h>0 U0 \( Z- h+ v9 k# ^2 h
#include<stdarg.h>
% \! ^+ T# ~/ |1 z; z$ K6 o+ Y5 mvoid tVarArg(int num,...);/*num为可变参数的个数*/+ A4 X1 W% c" g3 u) M
int main(void)
B+ C8 Q9 ~2 y" N{9 P% B! x e: G) {' l9 {
clrscr();
' L0 U( l' T, s+ _9 D0 G' L, ttVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");& N2 G! q% |( L/ f) \! `* l
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");1 G* V# T% s9 E; t6 [4 [0 D- g
getch();( I8 v# E$ v" n
return 0;9 m z1 L0 w* O2 v# K8 ~6 }
}) C& q+ ? V8 N4 I) F) j2 N
void tVarArg(int num,...). H1 e8 p: {7 i# y) t( F/ d# r
{8 M8 O, _4 e; U$ w
va_list argp; /*定义一个指向可变参数的变量*/, _$ Q3 ~+ I1 f8 `: k6 L8 {( R9 _
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
' h- G$ c# L0 |6 Swhile(--num>=0)# D& q' d3 o+ l. R% m5 x* M
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
! _0 j3 h( v# |# t7 B. L- v 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/2 ^; d% e8 V- L5 }5 K; [6 q: g: b- f8 g
va_end(argp); /*结束可变参数获取*/* B, B* I& ]' I
return ;) d" X) C* A: b9 A4 m7 ^" {' D
}0 Z8 c; M8 l6 T' h/ Y2 b
[; z1 i8 w! i. {' }8 l+ J
4)可变参数的使用需要注意的问题
+ C! Q) g7 |' o3 v
0 [0 p" h i2 J9 f4 L' \1.每个函数的可变参数至多有一个.; R1 h" z/ r( _! f$ P( g$ O
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.( i6 F2 r+ n) U& q1 u- L
3.可变参数的个数不确定,完全由程序约定.! @# x9 t+ B/ @5 u s. o
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.& D) m$ ^) o# E I, `; \& ?
而printf()中不是实现了识别参数吗?那是因为函数 6 A, |# O: e/ t- B, E
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg " Z+ N( Z5 X' M0 @. n6 ?
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
0 S' w1 s3 K( T8 f4 i/ x过在自己的程序里作判断来实现的.
4 B$ \3 R) t. E8 [5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|