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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
1 d; d6 A* X; n9 D它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔., ]# d  Z* A# @( F  `
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不# r' g) N0 d0 r2 h
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
% g2 m) T; A, V$ \9 d. H实际的名称与之相对应.' ~8 e8 O2 h( {" M/ `7 c2 [( A
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
/ @+ D# G4 i1 r/ g7 R* @- R. H9 F然而,更多地自由,同样也加大操作上的难度.
. d, a9 z1 A3 E# p以下就对可变参数的几个方面作一定的介绍.: N" `/ K% A$ w+ c9 S$ W3 {3 r
$ z* Q: |" A  ~$ n4 N1 t
1)可变参数的存储形式.( J& V# g% `; o/ K/ V0 ?3 Q! Y
0 ^6 `7 w  m3 A4 k; a
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,5 M  U5 \! Z$ j
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.- ?! n: r0 |3 s. D( O
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,% r/ D1 Z+ }9 p! x6 m
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.( j$ W/ O" [3 B+ v3 u; }! I
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
9 B  X% U4 K: m栈区:/ N4 a" W2 D1 z: |& [

' j6 ^, N$ G' V1 V|栈顶             低地址+ @5 V0 x7 d& v, n
6 f, D" q5 G7 [. T# o6 I
|第一个固定参数var1
- Q) \, \: {! U+ k2 |. n|可变参数前的第一个固定参数var27 {; b( |) z; G
|可变参数的第一个参数
/ ~% T! h# s' n$ v6 @( m! w|.../ g9 v) o' t/ t4 ]5 R" u& ^
|可变参数的最后一个参数
6 G" N. s0 U8 ^9 D6 t% V1 [|函数的倒数第二个固定参数var3
* I8 N' B* _! M8 N) b- c% T2 f9 K& O|函数的最后一个固定参数var4' ~: M8 ], h8 @* L, x+ J# k
|...4 e. j4 h4 z+ h" e/ x
|函数的返回地址
+ e! d8 z' [. C  h; K- W|...
4 z& ?- H9 p; O! o9 e4 X|栈底    高地址
7 @4 u3 `, \- J8 F
. D6 u% f- g( r. v/ f# P" ?1 g: m8 [2)使用可变参数所用到头文件和相关宏说明. N, W% r: i, x
2 D- `  y9 g* j% A8 z2 ?0 p" @( E
在此,以TC2.0编译器为参考对象来说明.
0 _4 x4 T& i$ v0 f可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.* s0 I, b- t1 d) Z
此文件为:
  m- k9 O4 @% v8 j) Y' `" d% f0 I/* stdarg.h
8 [+ c; L$ m. ^: }+ F5 i' y0 h$ d1 t- d3 q* M$ G1 w' ]* j+ {
Definitions for ACCESSing parameters in functions that accept* b4 l  E0 ^+ s6 L" Y
a variable number of arguments.
/ D9 J* J" o) ?! S1 x9 P5 _# F, Q- z
Copyright (c) Borland International 1987,1988
/ x" u" e$ p: D: w4 h/ g7 MAll Rights Reserved.6 T: Y. V" F, X  y+ ]
*/* B9 N$ E& ]5 W: S8 M: o
#if __STDC__
: B1 N3 n1 u, y+ r+ \, ~#define _Cdecl
  m$ I  ~$ F- g9 a6 W#else
7 `5 B, r8 `9 s#define _Cdecl cdecl! F% @0 N$ j% Q* @+ |" V! x
#endif
- _3 o! `+ D, ^; ~5 G) C8 ?! X- \/ o9 ?
#if !defined(__STDARG)' l3 }9 h$ Q' f- e% ?
#define __STDARG
8 t2 L. A6 F; m2 M% n4 u$ w3 i0 s  n+ j/ O
typedef void *va_list;# {2 r7 \4 b7 h) w) y7 l0 N6 O

+ y6 {0 o& h& f2 v; K1 C#define va_start(ap, parmN) (ap = ...)7 X& u4 ]; @) @( W2 F9 r
#define va_arg(ap, type) (*((type *)(ap))++)
! i) |  ^1 f+ m6 f#define va_end(ap)
9 q& X! C, H! E#define _va_ptr   (...)
! L! _4 {. g+ n/ S: z$ O# \3 w1 u#endif% m6 j  L- i. o6 g3 s7 ]
" G3 \/ M  c4 Q$ I
以上为"STDARG.H"的内容.) i+ P  J1 e7 n1 u7 x- R6 T0 L# A
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;+ A0 t/ y/ {2 p5 J$ x0 b
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
" y5 n! `2 a& j3 LparmN为可变参数的前面一个固定参数.! ?+ }/ w: f. \7 P; S9 e' W
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.  n3 F/ Y1 V- o) \
va_end(ap) 结束可变参数获取.
# b0 S( R5 A( }, s" C8 t3 h. e& |. ^0 ]" \& |
3)可变参数的使用实例2 L0 b6 k5 Z: y' u0 J
5 C3 c1 a1 [* j9 {0 P" c& o& C" ~
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.% A1 Q3 e. W, s# _3 g4 e+ q- Z( B
, w9 C( v% A& a7 G( F9 @) I8 m
#include<stdio.h>
1 r  U  V4 s4 I  h& [6 M#include<conio.h>; f3 k$ b0 y* h
#include<stdarg.h>
6 O8 u8 E# Y& c: n. q0 }void tVarArg(int num,...);/*num为可变参数的个数*/" n9 ^# h* k' @; g7 o9 I9 ?
int main(void)
' L9 Z2 _' Y0 }{! Z8 v9 d% z6 w. Y9 l7 M; v& T" P
clrscr();
1 W! I* {/ g1 G7 @3 y) K2 I# }tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
( M3 {& w& |# @$ X$ GtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
$ B0 f/ Y* G. c+ p) w5 y1 ]getch();
0 l* L' X4 m; i, O/ H: ]/ h/ x  [return 0;
' `. o) V/ y1 z}! r2 i# u* f. B. Y
void tVarArg(int num,...)1 V  u  c7 _. m; x7 j2 j* w6 p
{0 `% b: \9 m9 p
va_list argp;  /*定义一个指向可变参数的变量*/
1 I2 j5 E/ `: o. v: @& tva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
  S. G# |) r. Twhile(--num>=0)' c4 ]- l8 Y7 X, [* o5 i
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
5 U2 t: ?2 |! ~1 e; C7 C    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
* k9 C3 ~; \( @- A- o3 F7 cva_end(argp);  /*结束可变参数获取*/
, E$ w! H5 }0 Wreturn ;$ p4 {$ v" C/ U5 Y/ d
}
/ v* H- c" u2 S  L5 B; Q) f
: W8 p" _" h" [1 `" g) G& n4)可变参数的使用需要注意的问题
' j8 ~; w6 s' Q" h4 Z; z( L+ a7 \
0 ^# x# }# p1 p* U1 y5 N0 V1.每个函数的可变参数至多有一个.
. B' |4 s8 X5 s( I2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
, A1 v6 N- Q& \) {% T  e3.可变参数的个数不确定,完全由程序约定.  F# Z, ]0 g! ?# b; ~& m
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
$ o) y5 \/ }4 v. _& O& g0 }1 \1 R而printf()中不是实现了识别参数吗?那是因为函数
6 q# c- P: Q% `: U; K; eprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg / j3 ?1 u3 Q8 K# f' o* |4 E
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
8 ], M, i& @6 [1 _: @& @# }3 }过在自己的程序里作判断来实现的.
; g2 B! E* z+ q  r/ _" l5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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