获得本站免费赞助空间请点这里
返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.6 j8 N9 C% i- L% d5 I. k
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔., P; Y' I1 `( I4 Z4 P
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
3 R7 [1 ^$ Z) Q& T0 s定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
% E/ r7 Z1 g; ^- ^# p5 H& `实际的名称与之相对应.
; A8 f6 v0 Q4 A- U4 f! i/ [! h由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.6 A' k) }- M  W0 Z! B. Z
然而,更多地自由,同样也加大操作上的难度./ B1 V. d0 g2 V
以下就对可变参数的几个方面作一定的介绍.
  `3 m6 L4 L8 W+ I
. F$ h+ X1 l* _; Z+ r1)可变参数的存储形式.$ v' x) c( s0 i. _: K* P/ b8 Y$ l: l

9 V8 A# O( w- f$ l* _$ j大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,/ {* Z! A& p# e$ t
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.* l* [$ q4 _" J; e! D7 H2 ^5 |7 k
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
# q8 J& ]& O5 @* I& d0 h这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.. o9 ]" |1 r* o# e) l' m: ^
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):. Z3 p% R# \8 ]- w! k
栈区:# [( Q/ s, W$ O; g! |' n9 K
8 R1 F3 _- y2 m7 W# J
|栈顶             低地址9 ~2 d# |) u+ Q* J& B8 h6 r

7 ^: N! B+ O/ v|第一个固定参数var1( L- q# P9 s" i; [0 u4 w
|可变参数前的第一个固定参数var2! }( _+ ?' g2 q1 U( J
|可变参数的第一个参数" T3 ^6 j) M4 p1 E
|...3 s7 `' ~' f, Z( Z. @3 V- ?+ ^5 t' ]
|可变参数的最后一个参数
/ w7 ]6 n; W" o( c|函数的倒数第二个固定参数var3
( Q' v5 Q& T; I|函数的最后一个固定参数var4
/ P/ P" P4 u% P|...  I) n- k* a6 H
|函数的返回地址
) E8 f; a% C3 [: a! L|...) Q5 d0 Z$ m* o/ O
|栈底    高地址
$ K2 h% K, F$ N8 b) H& R* S9 t
3 F3 n: d! P  A2)使用可变参数所用到头文件和相关宏说明4 h+ w& P1 q  ^4 T* U3 @  J
  }0 l5 |2 x1 q! m
在此,以TC2.0编译器为参考对象来说明.
4 d' b# R5 O5 Z, a可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中., F; P) \1 h& b& m; F
此文件为:9 ]+ V1 o: J5 B& M
/* stdarg.h: l$ Z) Y) S" F

  Q) t" K- G5 [' E- GDefinitions for ACCESSing parameters in functions that accept
# S4 C- h& ?# j  L1 fa variable number of arguments.
; Z- ?3 i6 b! q) X, \+ W: i# `- N* P0 t% U
Copyright (c) Borland International 1987,19888 i" a0 l. ~. K0 I5 ^3 B' x. S
All Rights Reserved.
5 D7 x; h2 [, z  {5 {' ?*/4 g+ a* {& A! U
#if __STDC__- u5 T% [1 E! G
#define _Cdecl9 n; b0 ~7 O9 k' F
#else
7 r' a2 o$ p7 j# l1 f#define _Cdecl cdecl
- g, F: i$ n0 Q9 d/ J- C! \3 h$ l#endif( X% G* }& R4 z1 G0 P& u6 d

5 `. t4 _- t- v4 {7 F- L#if !defined(__STDARG)( k9 n; ]+ U) p: T
#define __STDARG; q& d1 \8 v* }5 R! @

9 D) D) q, }$ ltypedef void *va_list;
+ J3 d. B/ z' c' b  k6 C9 }) |7 h- v! m
#define va_start(ap, parmN) (ap = ...)" k! {, o" C$ L+ e+ M. @0 ?
#define va_arg(ap, type) (*((type *)(ap))++)5 h9 Q+ h' j2 U" ^: t. \' }& Y, }/ L5 H
#define va_end(ap)+ u: q# z4 w9 ~  L2 ~8 W) R! j  d
#define _va_ptr   (...)' J* ]9 b" E4 \' K1 O( S
#endif
0 E2 W8 h9 ^0 T1 G/ n
" ?/ J9 u1 P; j! Q- b! ]( n以上为"STDARG.H"的内容.
' ^4 U/ c& N5 D5 r) ^. c, v# m该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
# g' N+ e" t/ r% p; T$ x2 s4 `va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
6 f/ P" _3 ?# W1 E$ h; F* sparmN为可变参数的前面一个固定参数.
' d8 R- x, ~" Q; Q+ `va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
8 ~8 I. h& V1 u# ]" a0 ]va_end(ap) 结束可变参数获取.1 G! O2 W% A/ N% C5 R
9 Y. c; I+ J4 g3 i8 |; S" i
3)可变参数的使用实例
7 z3 j3 B, b8 O/ f9 c+ M! ]8 O6 }8 W5 r9 ?- Y3 V; R) X% p+ g
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.7 ]9 ?- w) ?- y# }- l4 B! }
/ k4 ^6 R+ u' _0 X( K" n
#include<stdio.h>
" ]# Y  Q& }# l5 H#include<conio.h>
% v1 |# Z2 N6 o#include<stdarg.h>) W* J; H+ w; W
void tVarArg(int num,...);/*num为可变参数的个数*/+ Z0 u) w" X! X
int main(void)* {1 Q) t+ ~2 L- M
{4 t* A0 t( ]: G. q. M" }
clrscr();
" D* I9 ?% G# a, B. E9 a; QtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
$ A3 ~  e( T' j' J  _) YtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");3 L( _- n% S9 n9 N2 v
getch();' ?' M& ~, }+ R0 d* u
return 0;
! d( t& U- }; j( K( u}" e0 A, K$ S) A+ p. ?: k
void tVarArg(int num,...)3 r8 Q: X2 z/ S$ I% N& ]$ O+ V; o2 i. {
{
: W" d6 v' g0 N6 pva_list argp;  /*定义一个指向可变参数的变量*/5 Q3 y# l& d" Z
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/; {- S4 O1 ^+ s. e/ O4 k& d
while(--num>=0)
+ m: r& ]# }( e- S  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
& h3 a/ Z* j& E+ H4 j% Q' Y8 D    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
8 x. I" k* O" G. z2 e9 ]1 I; H+ B2 Kva_end(argp);  /*结束可变参数获取*/, c+ q4 J, r3 H9 q
return ;
7 ^6 k" l+ {0 E" f& a}
* A" {/ Z% G/ j; o8 a+ [( l# J' N, v4 Q' X" j4 h, g  A$ y: n
4)可变参数的使用需要注意的问题0 h, V% Y# l; g! X1 g+ w
5 V3 ]2 {! P$ C8 m9 g0 g
1.每个函数的可变参数至多有一个.
- M" s( |  x0 o% y) l7 y# S2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.) Y6 o- z1 ?$ M! z1 U: w
3.可变参数的个数不确定,完全由程序约定.
8 n& `9 V/ @; I2 I5 n( t4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.7 m# H' S. E& J0 O, p8 }/ G5 j
而printf()中不是实现了识别参数吗?那是因为函数 & `( c3 |/ A3 I: ]; {
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg : b5 \$ V! `7 B
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
) P8 ]2 i2 f1 L过在自己的程序里作判断来实现的. 6 w$ ?1 P8 J  J
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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