返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.
% D) Z9 A( e' q% ^* a9 e它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
  S% X' \  ~- J2 `2 z% R9 P4 E8 ]( N可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不& U  U* d7 w) [2 g
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
7 _! V: X# M( t) E; p实际的名称与之相对应.( B. [( U2 n+ e3 o
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
* l% H& F8 [0 a+ x7 C, C' \( }- F; z然而,更多地自由,同样也加大操作上的难度.+ e* }  d# K" m5 ]
以下就对可变参数的几个方面作一定的介绍.' R8 ?. `: i: h, c

' V9 u" G/ V$ ^1)可变参数的存储形式.
+ \; w, u1 ]. Y* `' O$ u/ l6 l( C" y/ P* e2 T  V
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
( w1 `( \4 b, t8 X) n存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.5 P' D! j8 i8 I* n
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
5 }: f$ u0 p: K5 I- ~7 _& l, h- ^这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.9 M, Z" G. b( l. L% a# ^
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):& x/ I- p( _! c; g! z
栈区:
: H2 v' A, \7 G' Y- B1 n3 {0 \* V+ Y# Q! u0 R
|栈顶             低地址
, M" e+ S" X- J  {8 s& h
7 Z; v, X8 M0 d|第一个固定参数var1$ m. ]5 V' u" Y
|可变参数前的第一个固定参数var2
. O- a! V( L0 r5 A3 b  I) H& O|可变参数的第一个参数
) F7 ^: c5 {% B/ A% `|...3 p+ ~3 z5 l4 _- t" q
|可变参数的最后一个参数* p; x  }! c3 [) k3 e' b
|函数的倒数第二个固定参数var34 C4 i+ l/ L3 O; m) b
|函数的最后一个固定参数var4
7 Y' E( [- e1 @; C) b$ A1 d- C2 P|...
- t. v1 \5 v& @6 D  q% e|函数的返回地址
2 s+ P0 E$ y: J7 B' W9 X, b|...
0 ^/ t$ {9 I6 C* ^" f3 g& k|栈底    高地址
8 a2 A  f9 j. T
0 R4 k8 |, Q+ E3 N; f* O! e2)使用可变参数所用到头文件和相关宏说明
* l$ n0 l* s0 ]6 H0 \' Q2 `4 n* n- Z, c" b: K& Z* g( }
在此,以TC2.0编译器为参考对象来说明.
  |# i# m( [; s2 k; D' ~可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.0 k) {. D' F" I  o0 n! b8 n$ g
此文件为:
" G3 ~& \# c+ t3 S7 g% g/* stdarg.h
# a) G8 p& r0 j2 C7 i1 z7 `$ n: ~- @3 k( X; u
Definitions for ACCESSing parameters in functions that accept' `8 R! W# p/ M; `5 W5 ~
a variable number of arguments.2 `4 Q0 `0 ]( b% I6 m

. S, }1 t: e$ l3 _: S- GCopyright (c) Borland International 1987,19889 R, x, P" @: a" W, R7 l) @
All Rights Reserved.3 o9 \' j/ x% D! }7 p/ N! a" B' @
*/3 Q1 f4 K; n) M! w6 E, }2 \
#if __STDC__9 F) B4 q# ^: g. a
#define _Cdecl0 T. r. I- P6 \2 ]' n
#else! b  U' W/ V! {, L' e1 U/ m# G
#define _Cdecl cdecl# ]& P/ l! l2 q) r
#endif7 T+ L: |( ?( T9 {9 A
8 i3 Z; u) W, S1 g% ?
#if !defined(__STDARG)- i/ H( z! u5 d
#define __STDARG
2 E# y6 `' p% K# B
% f. S5 b8 q  l' E  wtypedef void *va_list;
" \- Z, u2 r  q
9 s) F0 A2 [  E5 v$ I#define va_start(ap, parmN) (ap = ...)
5 _% p3 V/ T" K/ d#define va_arg(ap, type) (*((type *)(ap))++)  p9 B/ K* t/ J0 F0 {8 y* ]
#define va_end(ap)
  r8 E$ B: M) p# U7 r- t#define _va_ptr   (...)
3 [, s8 d. [& @6 E" |#endif
$ O: k3 D( L. J5 a" g, ?4 t# G3 M* r1 H0 s
以上为"STDARG.H"的内容.( i+ [2 U4 }( A+ C. z, q
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
0 x* k* v. N8 a/ ~% Z0 hva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
" e/ _% M( I1 Y' o' SparmN为可变参数的前面一个固定参数.
2 k( i7 a& e! r5 m' kva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型./ `  |* ~8 C% U  x4 v% f; }( A( u! P
va_end(ap) 结束可变参数获取." z) c( P7 P. s2 \0 ^
( F! \; @+ R' |, X
3)可变参数的使用实例
, D  @. [9 T4 ]) m8 p. N& c& G5 T- g& |) }, l& S- _
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.: Q- A# \! c1 a" X# r
& f- K6 {% c3 E' k  S6 q5 ~3 [
#include<stdio.h>
. a$ \9 W& v  `' \; b* Q#include<conio.h>
+ d# p3 q& ]3 V+ c#include<stdarg.h>
, b3 ?! ]4 K* F1 xvoid tVarArg(int num,...);/*num为可变参数的个数*/, K0 k  D' b) z
int main(void)
# w* Y7 N  i. O2 y$ C  ?( T* L{
8 E% S- _$ r9 [5 m9 O, Z/ w  Mclrscr();
# ?5 O, ~+ A0 {# J6 B5 WtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
2 ], g7 I* C. d8 [tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
5 z9 ^7 ^: }! _: f7 }getch();) ^6 s, n  U3 g9 z9 C
return 0;4 w# z+ z+ \9 M  m1 [9 Q3 [( ]
}) ~' q7 I6 U$ k
void tVarArg(int num,...)
# o# _( Z; Z5 s: i2 k' P{  N* b1 F5 M# L' f# `
va_list argp;  /*定义一个指向可变参数的变量*/
! A: P. A. J3 Wva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/  l' K; C8 T5 B  e4 ]. L
while(--num>=0)) r) y" k! t0 v
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
7 S0 E) B  }- T* ~$ @- M    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/. h/ i" z. a+ }0 Z/ b2 M2 }
va_end(argp);  /*结束可变参数获取*/
( p8 t$ ^, C3 E: v( T3 Jreturn ;! j- a* b- \' I- \5 E
}9 e( ]% e; P5 C* N
, K% T& ]2 \3 D* a1 K: R; F
4)可变参数的使用需要注意的问题
/ l4 }( @* ~9 o$ R  f" k# o7 a: r! T  s6 g' _
1.每个函数的可变参数至多有一个.
0 W- p8 z$ ~, P. m9 O* N2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.: f3 Q" d% v, z! @& _
3.可变参数的个数不确定,完全由程序约定.9 I1 R" W& g  w" S0 l( [
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
9 M7 T, e2 V+ M: u( H7 T5 K而printf()中不是实现了识别参数吗?那是因为函数
6 T# h0 n, a! J( a4 N+ Bprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 4 d0 o( F5 o/ @6 ^# e; W
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 ! a; {' _2 C2 e% C* q- a
过在自己的程序里作判断来实现的. 1 `6 F2 U+ i9 K+ }
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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