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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
! j% d( P2 ~7 U它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.3 ?( B7 e+ v0 B1 s( e
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不* p: \  x+ I! C
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
% K" N0 |9 s  k1 X( q实际的名称与之相对应.& c8 Q4 G! f+ L3 u& ]
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
: c$ ]2 i, N0 t, L1 C- W1 D  ?; F+ P# j然而,更多地自由,同样也加大操作上的难度.
' _. ]: a& g9 Y0 L) L" c1 g以下就对可变参数的几个方面作一定的介绍.
4 M$ }0 c/ {! i4 Q
6 a9 a+ A, t8 E+ I1)可变参数的存储形式.
  K* c  F: O  [2 D$ }
6 c9 \% G; N' x" a大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
# y! k) p# Z- g& ]存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
  [+ G' X& J, M4 F/ a8 S在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
* t- G) b! ^  _. w& @这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.  P) L0 t1 Z0 X7 i
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):* T* |2 N1 C0 u! z6 b& t4 x
栈区:
% p' Z- D$ `& v' l  x* r: Y- w$ x. I# i( L' w$ }
|栈顶             低地址
9 I: a) Q9 u& u+ |* H
, m4 A0 B6 Y# c* z|第一个固定参数var1
2 A* R* E2 |. [- m- g|可变参数前的第一个固定参数var29 @! R) h' x2 a9 E
|可变参数的第一个参数
  ]2 k/ Y- v4 q7 K* O3 |6 S) t|...
1 g% ]2 V2 h/ @# r3 P2 i. n|可变参数的最后一个参数
. I  c. _$ Z+ D: b+ z|函数的倒数第二个固定参数var3! }+ ?6 P4 B2 c$ y' f# {: K- |
|函数的最后一个固定参数var4
3 y6 B" p2 X0 g& c+ f: I* D5 m|...
/ ^( d* k4 w" b6 \) w$ ?|函数的返回地址
' d4 n4 H( [, m|...
/ m/ ], {; _$ |9 {* \% U: C|栈底    高地址, g9 Y) A9 B, b0 R* X

& A6 D1 a* \9 B6 l5 p- D8 Y& x2)使用可变参数所用到头文件和相关宏说明
9 {+ L- k, \  V3 a, \: [" ^
; L& i) ~8 z: b# {$ s& V在此,以TC2.0编译器为参考对象来说明.
/ `6 [9 T# w0 b; L& \: u6 ^4 Q8 A可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
, X$ [# a+ ~) ]" Z" k1 K此文件为:" u+ y# A* }  q, W/ }
/* stdarg.h- M2 B( y$ @; ^7 \" F6 |
- f; V, A$ N8 e/ a
Definitions for ACCESSing parameters in functions that accept1 Z8 x, E, a2 }: M' ?+ y
a variable number of arguments.
. C4 q: _( j1 R/ r5 E. r$ J: _9 r
Copyright (c) Borland International 1987,1988: c* j4 ^* B+ b
All Rights Reserved.! z/ Y: j+ l, f# M$ B: u% J
*/8 E' B  U5 P3 A$ @& M- l* a
#if __STDC__/ t; q/ E6 H1 l$ p# I% `
#define _Cdecl
4 @/ z+ v5 v. @* n+ r/ c#else
! G8 ^3 ]6 ?# ]: o+ V$ \#define _Cdecl cdecl" F7 O* j) _1 W  B$ u+ s. G# e
#endif
# T1 A& y7 v6 D; W8 q. n) v
3 n2 O" O* Q# M& x/ M& \) i#if !defined(__STDARG)3 A0 i. c! h1 N% N% M- V
#define __STDARG
: b" h. c# k# t( ]
' |4 r0 s0 {2 u4 ~+ `8 f* itypedef void *va_list;" _. Q5 b0 ?0 `& M
+ k( a! w4 F; ~2 g  Q  l- {
#define va_start(ap, parmN) (ap = ...): C$ Y( c3 c$ U* X8 y; p* F: v  s
#define va_arg(ap, type) (*((type *)(ap))++)9 d% U$ K- h9 y2 j6 x. d
#define va_end(ap)
4 n5 ~8 g% F. ?, n#define _va_ptr   (...)
+ W9 Y! e! N! f, {7 ^#endif
7 Z9 `+ i* h' w- R& U8 d
! z3 E1 u- ]/ y以上为"STDARG.H"的内容.
* E6 S. E9 a6 ~  M6 u3 [7 N& _" d该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;( v1 J0 N* C/ W; K6 D/ j
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,7 T2 b8 L- X3 ]) o; ?6 _. s( D
parmN为可变参数的前面一个固定参数.% Z6 m. {' E5 y- X0 r3 ]) i
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.% j6 }2 N, Z" X# c
va_end(ap) 结束可变参数获取.4 T, h5 m/ N6 C
) Y9 C3 h6 S8 b4 v2 z
3)可变参数的使用实例
+ e% |- U3 @" i' v
5 F1 B" @( r2 t! p% E实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
, Y5 m5 V$ }& n. `- J# v0 j+ ]/ b4 I( n, r: k; p
#include<stdio.h>
3 E! }2 {- l9 e8 B& I#include<conio.h>
2 _5 r" ?: s; F- r( [#include<stdarg.h>
* F* Z2 z! h+ X- m/ yvoid tVarArg(int num,...);/*num为可变参数的个数*/  ?; A4 c. m+ _8 X0 u
int main(void)$ T- e- m* X6 I$ Q
{8 m, W/ m/ q' u6 _3 D- x
clrscr();
1 Y# d6 [' y# C( k& }" \$ EtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
- \* b+ W7 U. Y0 S; ]tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");3 D5 j2 y! [. j9 D/ i
getch();6 t$ T& ]9 l* J7 a0 h. E$ W
return 0;
# N+ x# @3 f% o. B* Z; {) C}) m7 z# ~/ M; p7 [1 \5 l6 K
void tVarArg(int num,...)
" @* Y1 u1 j* Y6 B" ]* a{. h" N1 v- \; d) v" ]7 b
va_list argp;  /*定义一个指向可变参数的变量*/) j; ]" _0 o" y6 I9 K5 g1 A
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/, B* z: u; p1 V+ b1 j% u
while(--num>=0)
( {# w) j# M; ]: z2 ^: q: [  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
  [6 J; p/ I0 b" O* c. G    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/0 U8 P4 Z2 o# A7 B5 x
va_end(argp);  /*结束可变参数获取*/& R4 |# c% Y  K  Z
return ;7 W, a- g' n! f) ?3 q5 t9 S
}
- N. t, I) w$ v5 K" W
/ a/ C; B# S6 T5 ~( T1 p, T, d* T4)可变参数的使用需要注意的问题4 K% u2 ]8 u! g  F7 `  I* C

$ U( U5 i& ]2 T/ s/ [" m1.每个函数的可变参数至多有一个.
6 S6 ?5 \+ R. \/ F; k6 [. P+ y" V4 V2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
3 y7 B! V' @5 ?/ H' |3.可变参数的个数不确定,完全由程序约定.. ?& `, q8 L6 X- Y5 t7 u8 o+ K. J
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.% c: x1 M# e" ^3 D
而printf()中不是实现了识别参数吗?那是因为函数
- n7 g3 N1 W# N( u8 Q3 Gprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
- A+ E' k  {  e0 B" c2 D的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 , M( j6 w# J1 m/ k7 b6 F
过在自己的程序里作判断来实现的.
0 J9 j. o  a! |& ?" b5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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