返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.
5 O4 [" Y( H5 N5 l6 ~& S7 j! p它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.$ j; {! R7 c, q- k% B
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
) z5 p) \" Q6 C7 ?定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
1 }; e, x4 c, j实际的名称与之相对应.8 L" C  A& j$ x1 Q; x
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.& ]; k; W  K& U) t0 ?
然而,更多地自由,同样也加大操作上的难度.9 \  i6 M9 X* d3 Z
以下就对可变参数的几个方面作一定的介绍.) s) e4 i1 `+ m5 M$ N0 M

. C2 U* S: a& F( e& S: M! H1)可变参数的存储形式.1 U) M4 i7 p# r2 q
9 B1 I. V7 s$ A7 G% X2 l* S
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,$ |$ |# Q; H: c  {
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
4 P& l) k. ~+ u在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
" h& @; v7 d' W+ O8 r这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.0 B8 k% T# z9 P8 X. A% A% O7 s. k& w+ r
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
7 {5 e+ f0 O/ a* ^6 A栈区:
. B5 _& j6 J4 f9 H# W% {- l% }' r$ A8 `/ @, v! G+ q7 O
|栈顶             低地址" n  ~' h7 [. ]4 ~% T

  V8 E0 L; i! t4 b|第一个固定参数var1
* m* k! a  o0 u|可变参数前的第一个固定参数var2
1 f9 T; r; A1 S/ ^5 U. h% `|可变参数的第一个参数9 Q- K( k. U' q. k: P9 d
|...
8 A% s8 T, k& E9 A  O, l& C4 D|可变参数的最后一个参数! ^* {5 K  A/ E5 E! l  G' _
|函数的倒数第二个固定参数var34 s$ Y" N1 y% U2 Q% T
|函数的最后一个固定参数var4
0 ?% z: ~4 k+ B|...
& r7 E& v9 L2 b& R* w|函数的返回地址% N* I- v8 ]% z5 E" K- i$ L% j
|...
( y$ N9 i0 H$ _0 m9 P/ _|栈底    高地址
* o2 X2 [" Y+ Y  l- C4 V
- N  @7 `5 Z2 X9 W6 N$ q2)使用可变参数所用到头文件和相关宏说明
$ ^9 L, q+ E  P3 O$ R
: E1 i# N) L/ I1 v, F: E在此,以TC2.0编译器为参考对象来说明.7 l/ t" V6 k) ?8 K* D# M& L
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
# e" D0 G1 V; |- X" u+ w6 i此文件为:
  T' `3 q5 F$ ]% }/* stdarg.h1 x' ?/ e$ m* Q

8 Z; c! x! X% ZDefinitions for ACCESSing parameters in functions that accept
. j. s% g- Z# F; [& ea variable number of arguments.$ g  L3 `/ y1 [
! c3 Y( B/ v2 ^, M+ l: i7 W$ t& p
Copyright (c) Borland International 1987,1988
9 j$ P* G2 M9 F3 N- VAll Rights Reserved.& Y( [$ I* X5 |' c6 I
*/
8 o9 H6 l, J$ n* a% N! e: c) S#if __STDC__
" ?* M9 E# v, `; R. e#define _Cdecl0 A* e  m1 O, ]5 @
#else% v  f" J# X7 H2 I, S: u
#define _Cdecl cdecl
$ [$ x/ q) A% q7 w0 H0 e& _9 N#endif5 b) G/ o: n/ M
3 i2 x, @2 E* x9 L
#if !defined(__STDARG)+ r5 k4 R3 Z& I: D! Y
#define __STDARG
' v% V7 p* Y: \) n" \. N) p/ X! P2 {6 H4 E
typedef void *va_list;
" H, a8 y: m" C0 X  }- y6 h" i! t: Y! H& |, i, Z7 w# @% ]" g
#define va_start(ap, parmN) (ap = ...)4 z. m1 W3 o2 C  K
#define va_arg(ap, type) (*((type *)(ap))++)1 E. Q  r7 I; l9 P$ n
#define va_end(ap)0 B& P6 D8 o9 ^# ~% [1 A# F
#define _va_ptr   (...)
' z# A3 \2 U( v+ R& @#endif/ }; J1 ], ~5 t# {) L

' ^  @' _: W% o# l/ N1 ]以上为"STDARG.H"的内容.
  X& F) J/ a  U; S4 W7 _0 g该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
5 F4 z" X3 l; J2 w) T& `, fva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
9 k) G3 @4 H) j" f  K0 sparmN为可变参数的前面一个固定参数.! m  R0 [- i* C4 e( U! N. S3 [
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.* Y' d# H+ B$ ~# z, ?
va_end(ap) 结束可变参数获取.2 G- \0 z# M" {3 \# h
# Z! w( i# ]0 n# j0 K+ j/ O
3)可变参数的使用实例
% O" J2 R( F% m: D& r& _& ~, Y5 D( c" L; y: D( c( C
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.& r6 s7 M7 W% j" }! W9 N1 w& f5 f
# ]8 `/ @% I! ?# Q5 P
#include<stdio.h>/ @3 Z. V0 q; z% d
#include<conio.h>
; V- C! B& t& V& M$ z; x#include<stdarg.h>. }$ `# z  i- Q2 ?# G
void tVarArg(int num,...);/*num为可变参数的个数*/& @- t& L4 x0 n
int main(void)
+ A2 \! q6 }7 \" z{: Q9 B0 {2 ^, ^4 m
clrscr();$ o& C$ Z% z+ W4 \$ d
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
; f" j& N6 }1 x& P7 z3 C' j2 ?2 A% ktVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");( l) X" w) n6 x  }
getch();
1 g4 m- o0 n7 }, m' \: C  @return 0;3 ~( M5 \: u' O9 S
}
8 h! }: A$ |7 ~7 M% b9 D2 svoid tVarArg(int num,...)0 ^$ i, u+ @- J9 B
{
8 `& S/ S$ N% H5 F/ t" b: Rva_list argp;  /*定义一个指向可变参数的变量*/, M5 p# M) u: S- L/ z
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
1 `+ M. M6 o+ F' V; gwhile(--num>=0)
/ G0 F$ }7 N) [8 e  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
  q9 k; Z% ]6 U# X8 D% @    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/2 p- `! d3 P( ?1 A- u$ k; X8 L
va_end(argp);  /*结束可变参数获取*/2 z' c. }; r$ E' b3 Y8 q
return ;
3 G7 y; n( U" }2 j# @0 ^2 o5 O}
. a6 V7 y) m% {8 G/ P0 ?
+ N3 B7 l+ s- P% k; F# Q. A4)可变参数的使用需要注意的问题
1 Q- t0 |, M# a1 P0 H) F$ F
6 k7 _7 y- B' f" H+ i" n# Y# H$ W1.每个函数的可变参数至多有一个.
- U- s# n4 m2 \: c3 n2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.' f5 l, K. V* e( A' R
3.可变参数的个数不确定,完全由程序约定.
$ W6 H1 o  N6 I+ s4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.' f3 m4 b% Q3 @( R4 H& b
而printf()中不是实现了识别参数吗?那是因为函数 5 [+ v+ G0 ~% r. b0 n4 [: C5 ?6 N
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg & Q8 q5 B# ?+ t- G& A7 G
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 ) |0 h6 J$ a% d. U6 T
过在自己的程序里作判断来实现的. + a) R% p( Q+ z7 o" }* e
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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