返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.
8 A8 Q/ Y; y0 G4 F它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.5 Q- q  I9 r. m- t8 t  b: _/ _
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
7 A$ \& j+ n$ ~1 z6 }定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有) b, {4 j" L" }. q6 ^, U! S
实际的名称与之相对应.
% y) g7 ?% {1 r8 f由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
1 x% @: ?1 p6 J; S然而,更多地自由,同样也加大操作上的难度.
) ~6 k. N# H* n以下就对可变参数的几个方面作一定的介绍.( d# b' ]6 ^9 R7 ^6 w. X; D1 h

9 E8 Z" y& m7 a8 {8 }1)可变参数的存储形式.
4 C  @* @. p4 X* j9 g# H0 ?" O: X4 h4 F
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,' V/ u  L$ R( J" Y& d
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.1 k1 |, h% ~: R6 a/ B; T5 b
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,9 U8 M0 ^' e; D: [5 Y
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.2 }" j2 o0 E; M. ~! z
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
, w0 @/ o/ p# }% l栈区:; g# o2 O6 _* n6 h

3 C0 U- t5 u" @: o" m|栈顶             低地址
9 V% l" c+ N! @* h# G" a& t
/ a! o( B$ r" a2 H7 G|第一个固定参数var1
2 r# {3 ?2 O& ~' r|可变参数前的第一个固定参数var2
9 g! F; |: T+ |* i1 d2 [# Y|可变参数的第一个参数
; p+ w8 l: S4 H. t7 t3 C# Q0 G|...
" k8 u9 S" |4 Q8 Y|可变参数的最后一个参数
1 a! ]& {4 n# O, h|函数的倒数第二个固定参数var3
2 T( n; P+ i" s" z: u|函数的最后一个固定参数var42 l9 A. K2 w1 B: g
|...( W# P- H) r0 ]; q# H
|函数的返回地址
" O2 G" b8 R- `8 w+ `|...2 u/ Y. M1 V- g/ K$ \
|栈底    高地址
8 g, U/ S% {9 T! A5 M' n6 ]+ }
8 l  S& ]" O  C2)使用可变参数所用到头文件和相关宏说明6 G6 o# y9 J( d& u7 e

2 ]$ m7 E: S$ L4 y4 q- I在此,以TC2.0编译器为参考对象来说明.
& O& a4 g! D2 f" e0 H3 U7 E可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
  v8 V' ]8 `$ p此文件为:% F6 W& Q8 @4 u" j9 y. K1 H7 u
/* stdarg.h
3 i# y1 o* x* R% ]' {% [8 k6 U9 f7 x' j% W" S
Definitions for ACCESSing parameters in functions that accept
& i7 u( Z, {# m. E6 X1 ta variable number of arguments.
1 r7 F" [& j  S& z3 ~
1 j5 n" q( c' w; Q. W# P3 o) x% wCopyright (c) Borland International 1987,1988
+ T( F- B0 N, jAll Rights Reserved.
+ [, B4 p' R9 d3 U2 _4 G*/  Z2 h/ h; e+ Y& i
#if __STDC__  Z* t: }. V3 s) Y+ T6 X$ Q  X
#define _Cdecl6 g4 T1 }: r: i) m" U
#else
4 t$ x" r3 B+ s#define _Cdecl cdecl; M! f' g9 j; C
#endif
8 }" t# i% J- y8 v3 n. U
$ A  K, [; |; P; I1 f#if !defined(__STDARG)2 v2 ?1 v3 v5 T" Y9 H
#define __STDARG; P8 a/ N: h6 U2 ^1 m  [
: g# i" [# i1 S. A  D; \6 j5 V9 [
typedef void *va_list;
' ?3 i( K7 ^9 _  D1 i% s& p- Q" C% j  j
#define va_start(ap, parmN) (ap = ...)% ]4 E1 L0 W0 P8 ~- M! B4 I! K
#define va_arg(ap, type) (*((type *)(ap))++)
' z& \6 n) ?1 J# [8 l; J$ U#define va_end(ap)& f, A5 f+ S. C* o1 i
#define _va_ptr   (...)7 c0 G% ?$ F+ s$ A% {
#endif
: J) ]0 t$ Y/ {3 J5 ?/ e8 M" @1 a! S& s, o8 u
以上为"STDARG.H"的内容.8 e* n; J# n& _( x4 v( t9 p  S" G
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
- M4 n) d% w& `1 p6 t  ava_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,0 `$ a6 e% q/ B: C
parmN为可变参数的前面一个固定参数.
' J( z6 c8 e( Sva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
9 Y" ]5 I. z5 C/ }va_end(ap) 结束可变参数获取.
0 S/ I3 u0 `7 }& d; r0 t
& D4 F- m# I7 U' A0 J' z3)可变参数的使用实例! U2 v! C( S) r; a" _0 y$ t
# f; u2 ?! Z1 `% i; x9 P
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.0 j8 |& H  l* P; w
# Q: R# l: `: a- N( K4 K
#include<stdio.h>
: L! M; `8 c, ^' K0 [$ T( Y#include<conio.h>; i2 z# {# z0 W; E/ s  |
#include<stdarg.h>+ |+ M5 m, o: [0 R0 o  [+ E
void tVarArg(int num,...);/*num为可变参数的个数*/
1 _" \, Q! J6 I: |int main(void)
" H( a4 y' K" _( s8 C5 E* p; h8 c{" t' ~; H3 J5 B: q7 b0 m
clrscr();- ?0 E0 R0 C3 O; z, H' V
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");4 R4 U$ `+ ~# Q: b- Z
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");) N) w! k' E6 k. \& E/ T9 O
getch();0 |8 d* {* I0 g; I5 R- p& k
return 0;! B* a4 k, @/ \
}
; k4 W) i4 u4 i9 [  Gvoid tVarArg(int num,...)
) j2 s+ f/ @: L& W7 n5 @0 }{* `5 K- D% |1 i3 |1 G1 i
va_list argp;  /*定义一个指向可变参数的变量*/( @$ K( D" Y5 i  K& E7 F+ W
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/* h4 c0 N' u/ S+ W: H$ ~& i
while(--num>=0)4 y" |6 a( D1 _! L: L( f! [0 c
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
4 `# w  |- F) W6 v; y( q' o4 N    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
% P# l& k( S0 T8 I0 s3 @: q& k4 ava_end(argp);  /*结束可变参数获取*/
8 P- A) y5 c+ F. B+ {3 Freturn ;
0 r( y1 @9 K5 K5 ~3 ~2 P}
' B0 C5 @) q$ ]. B8 t. B/ R! W
! u8 v5 @, h5 p! R' y* n6 z4)可变参数的使用需要注意的问题
5 P( p8 x/ }) T% P. {# I: r! r- n
1.每个函数的可变参数至多有一个.
# I) o1 B2 ]) P, D0 H6 w2 Z9 L* q2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
5 V0 N4 }! u9 X, K" n3.可变参数的个数不确定,完全由程序约定.: R. B% u( t5 }% ]2 S& M
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
' M' v- \" S. d1 h' s而printf()中不是实现了识别参数吗?那是因为函数 8 @( i3 [6 f  s- M
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 5 f8 H' W. G# D+ L* _
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 0 X& Z8 N, _6 G* P0 e: `
过在自己的程序里作判断来实现的. 2 _! h# t7 G' q4 I" _
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

返回列表
【捌玖网络】已经运行: