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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.' K* b5 U1 f S
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.0 b# i. ]! ^. G" s a
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不+ f4 D! ^4 ~1 P9 _/ j1 S
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
# X/ f* d, l3 k8 v* O实际的名称与之相对应.8 O @; T1 u/ L/ c4 p, V/ h' l7 `) z
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.$ ]# L* d7 N4 u
然而,更多地自由,同样也加大操作上的难度.0 W$ } }* i' Q; X" ~# f3 B9 p6 h& _
以下就对可变参数的几个方面作一定的介绍.! E# A1 V- n3 u2 V+ \$ m
$ i# b1 o4 T) l2 c- `/ w1)可变参数的存储形式.
. \7 O% m5 Q: S, p4 R; u
2 @7 F+ J: U) b( Z2 e2 ^大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,# |7 s4 a# x2 d s4 i
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
) C2 I1 O1 J8 ]" I3 B: z: }& h# p在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,6 y4 w6 U0 H1 E( l
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
@; s4 _8 S, i. i2 f因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
, N; j2 f5 k! b( z% _8 o0 L栈区:
" l ^) O. H( @! t/ b& k
. W; h7 g) p* H|栈顶 低地址# Q8 _/ M# r, U& k9 [5 e# s4 F
, I0 n0 P' _" C( }2 K. C. |* ?, R
|第一个固定参数var1
3 p+ ~, w; Y8 f|可变参数前的第一个固定参数var21 y, W, }' k% O' _* |
|可变参数的第一个参数
V t6 T T6 u# L3 x7 H: q|...
# Z9 k9 x: Y0 o9 y4 V7 y$ L|可变参数的最后一个参数" p+ E2 l5 I, M$ o+ @
|函数的倒数第二个固定参数var3
' o+ P$ W) b$ O6 l: ?# t- q|函数的最后一个固定参数var4
+ T' t, u: r7 E|...2 K5 b. B' a; z
|函数的返回地址
, o$ ]2 [+ I* B5 E|...
/ \1 ?% m6 m0 c3 w5 w* `|栈底 高地址9 q2 q8 D& F& e: L. S
4 e4 q2 G" P& m" T" W" y5 k
2)使用可变参数所用到头文件和相关宏说明
9 l M9 O8 U0 v2 g, P R& n; a
^! D* c; B4 U {8 @& {在此,以TC2.0编译器为参考对象来说明.
( {3 H, u4 @9 e" ~: e) s' s) \" l可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.8 O4 w7 G. H. }" u4 F r9 L; `
此文件为:
+ S1 D' k& Q* |& ?, n" i/* stdarg.h* A* |2 Z0 B, Q9 F
/ h# H: a ]( s3 ~* t6 R
Definitions for ACCESSing parameters in functions that accept
& S* |- g) B' }- Va variable number of arguments.
) O/ [ n1 q* m& o" H/ t) ?) m
* _; z( U: }6 KCopyright (c) Borland International 1987,19887 U; _; }, E4 z: A) Q% @. p
All Rights Reserved.# d S4 P- i) }& ]" N/ z1 o; M) n
*/* @. y! J X a% J6 q
#if __STDC__
/ K. K& X' y" T% X- h% z/ K#define _Cdecl
' r8 o1 I( Z* x* b% A" ^( I#else; Z9 B' f0 l- }' _0 P& c& q
#define _Cdecl cdecl
5 `, k9 j. V& L% Z; w1 }#endif
8 Z# P- R* Q7 y1 q0 y
+ Z( o% F8 u. H#if !defined(__STDARG)7 | w* v, Q `. D, W" h4 R
#define __STDARG
+ `& R, W" ~ [+ D4 u: B' c+ R; K2 x" u- @3 G1 L* ^( `
typedef void *va_list;
$ `1 Q1 `; l9 W: i# m# P% I% Y
% q; i, q3 i+ E4 t4 }+ t#define va_start(ap, parmN) (ap = ...)1 T. h7 ]. v( |. e
#define va_arg(ap, type) (*((type *)(ap))++)
" U1 U: `- {0 M( Z3 t#define va_end(ap)
4 J; E$ o3 N( A n& n8 \#define _va_ptr (...)1 k6 `# v0 I! Q. F- I& T
#endif
0 O6 s- s5 [7 F: J1 V9 F# _* g, a7 O9 }
以上为"STDARG.H"的内容.
3 o; f! }0 O/ M7 m% I该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
1 e) h6 G v- p Sva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
9 m% X7 k* |+ q6 t ?' TparmN为可变参数的前面一个固定参数.. t L `2 O* X, _' A
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.& n& n& l/ U4 T2 \( c z
va_end(ap) 结束可变参数获取.
5 I/ D. J& p" J7 D
; o+ _# k4 l! w3 J3)可变参数的使用实例& ]. w/ G8 d; R5 G5 J
. t. m% Q5 s0 t, r
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串., Q; S" I- Z5 h |, m( ]
1 t0 g. o9 D/ M0 E9 L# |
#include<stdio.h>* ?8 W0 v! q& ^( k! D4 U/ T
#include<conio.h>- ] q \% u% p, z: h8 w5 g0 y6 ]5 X. z
#include<stdarg.h>
; U! ~5 M5 A, i) v% f! q' ]7 vvoid tVarArg(int num,...);/*num为可变参数的个数*/
" u' L6 W( c$ x$ }+ Fint main(void)
0 L! i2 P5 l* ?( x/ q{
( o; v" t! L8 Y$ G: c5 Zclrscr();, k }& J1 B6 ^; R. y
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");+ z+ w. P, w5 l
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");4 b! M1 E/ M$ F: l4 }% x9 s
getch();7 a' b, ?$ K$ z7 \. \
return 0;6 P9 c( |" K* d$ |; H
}
0 q- a' q. F! }* Vvoid tVarArg(int num,...)
; K2 `- j0 r* ]% O. K' i{
0 Q i6 D/ E9 E9 I# \va_list argp; /*定义一个指向可变参数的变量*/
7 V+ w* W9 q( S# E- j! Q9 Sva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/3 n( w0 g0 G1 [% n& r; x5 z5 b
while(--num>=0)) U# z- H4 G$ C5 n2 P5 ~
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,2 g" n* T) f4 y0 i" L! |
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
h9 W7 e% Y1 {va_end(argp); /*结束可变参数获取*/7 D/ S* P4 K5 O, Z& m2 T
return ;
3 k' {1 W- O; N( P, b}
$ H) g7 K# A# F/ i# B
o% }# W5 J5 Y M' X4)可变参数的使用需要注意的问题
5 F, I |1 m6 A* U/ h7 M
. `/ y7 i9 n6 T; L9 V4 ]' ~1.每个函数的可变参数至多有一个.9 f9 L+ g0 }4 G" a$ }9 u9 z
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.8 [* Y) h/ f5 W4 L' ]
3.可变参数的个数不确定,完全由程序约定.
_/ i0 z8 [. P; G: x4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.* N3 ]& ?9 g& q
而printf()中不是实现了识别参数吗?那是因为函数 * J( r- d" k* R/ m
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
' k5 ?" Z. k7 f) r' q. g! l的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 8 W+ f- B4 F. L0 w/ ?) P6 N
过在自己的程序里作判断来实现的.
" m/ z! J+ q* ]& c, ?" S5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|