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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.& j2 z" l) d' ^2 n4 t+ s9 f
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.' G( X4 d; ~ ?- k9 P D" ~! o$ s
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不 n, V/ Y7 s( H6 T' |
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
# S8 ]. O2 h, I实际的名称与之相对应. Q$ ]/ a. [% o7 S
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.% r/ y. f2 s6 S& N1 L
然而,更多地自由,同样也加大操作上的难度.; [. a1 ?; K* \0 I
以下就对可变参数的几个方面作一定的介绍.8 D- L$ l7 L* X( H
8 H) P$ ^. z, G1)可变参数的存储形式.
* ?- I7 P& C$ m* R$ u/ _9 W5 @& h% Y, s, Y
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,9 |" D. {% ?: v1 D5 G3 w
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
5 l4 A3 B. w! i2 x+ |( o2 A* j$ N/ e0 N, O在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,* g3 M6 v' x# x! R* G( l
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
! ~9 Q k2 D4 [$ U- o* B' \4 g因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
" W1 Q' ~& A/ _. Q0 Y栈区:* f( C. t* L% @! N& f" g
/ B( n6 N& g( G( c|栈顶 低地址0 {" q0 A5 w& X1 i
: u: M( U. p( j5 I& c
|第一个固定参数var11 s( N2 F9 O( t' V4 @3 H
|可变参数前的第一个固定参数var2& E( v2 d( m/ q7 T6 ~9 B m- r
|可变参数的第一个参数" u- Q5 g4 {+ }% w8 F
|...- x3 y) N2 e/ j4 \: u
|可变参数的最后一个参数; o% ]1 {* Z3 K3 `
|函数的倒数第二个固定参数var34 D& _( _! ^; {8 ^. H' p
|函数的最后一个固定参数var4
) i! g0 R8 `& L$ f1 q* C6 X0 Y|...
( F- o7 B0 l# L7 Z% K|函数的返回地址
1 s! m) L- E: s# l0 [& S. e|...
& H5 j s+ T4 N4 I) d+ H6 U|栈底 高地址
2 Q, D" f, d1 r
, n" [+ r3 H0 H( B+ N2)使用可变参数所用到头文件和相关宏说明9 E* J# l3 Q9 d- _
1 m7 r4 g" @2 C2 Q
在此,以TC2.0编译器为参考对象来说明.
# H, e, r8 M5 { @9 u2 Q可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中." n1 r. ?3 `6 n/ s* v
此文件为:
+ u) S) n% J; l$ Z) @4 v/* stdarg.h
0 b, {+ A0 Y) i$ @3 B
" `& h% l- S/ l% pDefinitions for ACCESSing parameters in functions that accept
, Y1 j# ]" ?3 m3 v9 |a variable number of arguments.
4 q0 A3 f$ i4 E% w
0 z5 ~! S) n9 F1 m7 BCopyright (c) Borland International 1987,19883 e* p: M, P S$ E' x: k
All Rights Reserved.8 g# f3 `8 @& L2 G
*/
, h6 _( h6 A( R* |#if __STDC__. M; z1 Y* I% j# J) }) ]
#define _Cdecl" f5 V) k; Z1 \( l! i
#else( u' n- U0 l, ^- u( F4 Z5 V
#define _Cdecl cdecl
% O5 v, m" q% \; e#endif
8 n9 E, W' ?/ Z& u3 \* t* H, ]% v3 A& \8 Z2 ?; l
#if !defined(__STDARG)
, V, O0 u" L: l& ?( n#define __STDARG: J' A- ~( A1 z* H. N4 U- C
{" Q I. o! U8 Ytypedef void *va_list;! N- ?4 H# e! E( x
p$ G; w* l% Z# g5 d0 B9 A5 m
#define va_start(ap, parmN) (ap = ...)! Q; T- G3 K' {
#define va_arg(ap, type) (*((type *)(ap))++)% n2 ?* i- p) ^; P( w0 z
#define va_end(ap), l+ @& A" ?4 w6 Z; P e
#define _va_ptr (...)$ d" J3 s4 I/ b1 _$ ~9 j
#endif! U. ?+ h. r( c& o
, s- D3 X0 Y' X+ ^以上为"STDARG.H"的内容.
& ^" N: l4 q7 J5 N4 K4 K6 ]该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
. X. }4 C! r# [/ V& b& @va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,, a6 Z9 g0 f' m+ H- i* B8 ^% ^
parmN为可变参数的前面一个固定参数.
$ u* t0 H( h& L1 R/ {va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.) m: S G- Z) }1 r7 L0 s3 w4 h
va_end(ap) 结束可变参数获取.
' A4 Y. k( q9 Y0 l! v9 d/ i. w
# V3 z# g3 Q$ t3)可变参数的使用实例
/ g4 P t; a) `) s* ~: a
8 n5 y+ L! n% }, A1 E& q0 L实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
8 a% f3 ]% C' j% o
; J, n5 D4 M( d5 C6 ]#include<stdio.h>3 p8 \. q- a' v) x! I3 W
#include<conio.h>$ N, {5 R9 o4 H8 [6 K) l: V
#include<stdarg.h>" R. E5 w- n, y
void tVarArg(int num,...);/*num为可变参数的个数*/
" P4 H& W5 {9 {, z4 \int main(void)/ f' i2 W, o8 p l3 T4 C% ?" U$ O
{
9 q$ K/ T$ w; ~% B' |- e( fclrscr();! c1 i5 l( D$ C: z# \
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");2 @& U) Q* Z. ~; y& V
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
) ? |( e5 V: k% r0 ngetch();, l( t7 D5 U( I ]3 I$ j. ?
return 0;3 ?7 ?. Z- F: h) r% R; X# o+ n7 a
}
1 d' F, v% A' Bvoid tVarArg(int num,...)9 l1 V4 k( g& j. Y; O2 T
{' R( R( k/ q, E
va_list argp; /*定义一个指向可变参数的变量*/5 A# X+ s: R2 l; m2 _/ z
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/$ K$ [6 t- k2 f
while(--num>=0)
% C e- j2 X' h+ B( {: W3 f$ { printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
) {5 I* M! n& N$ @7 n+ u 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/8 y2 S" s: x+ t7 _0 U' k! L
va_end(argp); /*结束可变参数获取*/
$ B g4 i. v$ } k; {return ;
2 k. W' B9 |0 \/ ~3 r$ p/ ^}
' e5 F( r. `- `9 D& {! o7 R' |# M) K* S6 E+ j
4)可变参数的使用需要注意的问题! D8 }/ w6 }" O' S
0 l" K5 T/ T. C
1.每个函数的可变参数至多有一个.
' ?- i+ j9 f. T i1 P; D; _% P! y2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.# b8 _2 W$ \& O
3.可变参数的个数不确定,完全由程序约定.
1 k6 W. x' R) R E6 p3 |4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
$ y% z7 r6 l# [2 v; h而printf()中不是实现了识别参数吗?那是因为函数
! a* v1 f" f; v- W4 hprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg R! D& c# l6 Q
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 9 v' h, U3 W: e2 B! l
过在自己的程序里作判断来实现的.
3 R8 h" d& s: t7 p" Q/ T7 _5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|