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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
/ I- E' b# I8 L3 K8 E3 @5 b它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
+ w/ F3 x1 i3 n8 \可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不  z) P1 q* }' l- Q+ Z" d) j& ?
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
# _: T1 m  o3 @3 z" c实际的名称与之相对应.
% Y, h" c' P) W) {4 q: G' V由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
6 S0 c$ [6 O' }然而,更多地自由,同样也加大操作上的难度.' e0 y; b" E- R. @
以下就对可变参数的几个方面作一定的介绍.
4 }  n1 [- _; _9 f* ]5 p8 E3 s0 [: `* F
1)可变参数的存储形式./ O" U; R/ s8 ^' e2 J
, B4 [) z1 u+ h' Q: ^' d+ J
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
+ S7 I5 U3 j( N* s存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.) A/ ?8 ^7 T3 O% P
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
2 P3 u3 [# ^4 u这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
# ~: \8 Y% A& b6 a! g) U- N因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
5 w" I9 y0 \8 H2 ~栈区:
0 l1 X# x" {$ }! i) D2 k+ I3 @3 U: e. q7 `+ d" K: j9 p3 ?
|栈顶             低地址1 F) r- M2 k( D6 _. Y/ s( Y

/ {, `! P) \- O# @% z3 r|第一个固定参数var1
9 }! N/ p! ]) ~6 g8 n9 q7 I1 o|可变参数前的第一个固定参数var29 ]' t  ~- b- c5 S& G- S3 d
|可变参数的第一个参数$ |1 F  r; v) N4 R4 ^8 Q6 O; N/ S
|...9 [: J6 ]+ K6 w0 ?( k% R
|可变参数的最后一个参数/ o5 ~3 R8 v( \3 H: l3 h* B. n
|函数的倒数第二个固定参数var3' K+ \7 x; L# Z; p) {
|函数的最后一个固定参数var4$ e& Y& h: _$ U8 Z0 T
|...
9 l$ z* ?% ~# E. m. K2 Z. `3 ?) J|函数的返回地址+ Z* U' W. s- o) U! L
|...
2 l& V( l/ z2 }9 A: D( W& o( o|栈底    高地址
8 A4 Y2 s8 i, u. y" S3 R" F4 x7 c* s0 ^8 x
2)使用可变参数所用到头文件和相关宏说明: p9 G2 K  W) O" P' y4 M" V

9 ]- m. U5 M3 \* @  Z9 I* @在此,以TC2.0编译器为参考对象来说明.
# A. ]0 P7 a% |0 p可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
' P) `1 ~4 h/ `: [  b1 w此文件为:$ T, G1 Q% |% q) v+ h$ p
/* stdarg.h; g% e- U4 h( S
3 y$ |3 k1 S( [/ q
Definitions for ACCESSing parameters in functions that accept
* i1 [- `- H8 {3 {! p8 Pa variable number of arguments.
* Z7 H; |9 G$ k) p1 I! R7 B- a* t+ R8 o8 k0 |
Copyright (c) Borland International 1987,1988, |# K' ^6 t0 a- M- E7 ]
All Rights Reserved.
! n- u1 T$ D3 \- p* Q' Y*/
# x8 h7 |% j! |# j" y#if __STDC__
" ~! @8 J. j- C% z2 m#define _Cdecl
( U3 S1 T! e; q- y#else
7 _' X6 U/ `! V" U6 M#define _Cdecl cdecl* u7 S7 U3 C  m* d' F! ?/ p
#endif
  c; s; n" ?8 M  Y3 ~" ~9 G3 K; v: ]) I( W
#if !defined(__STDARG)
6 w" V3 m! S7 q7 r: Q8 t#define __STDARG0 O0 D2 Q5 m3 ?$ F/ t: c0 P
3 b1 U! m/ {: H$ D
typedef void *va_list;
' V$ S0 U  r( k7 L! x% [
( _% P# D! e' e1 G! v9 m#define va_start(ap, parmN) (ap = ...)
: ^7 K# H" B/ `2 @& s#define va_arg(ap, type) (*((type *)(ap))++)* k' Z( G  L- q1 H
#define va_end(ap)
: J6 y9 m; N3 G, r6 |+ U- Q#define _va_ptr   (...)
6 P$ R$ k  y: k#endif
. [" }7 n3 u: o4 {9 l: ], I6 G1 M8 S7 i6 |: E; a
以上为"STDARG.H"的内容.
& x7 P5 s6 O1 |0 L该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
) B3 E; f: P, |" ]: S$ Uva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
2 X& S# k+ J8 E7 v$ U* R( sparmN为可变参数的前面一个固定参数.
# W5 P) W# c! e6 ]) fva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
9 j0 C# ]: N$ Q4 J3 f1 \8 bva_end(ap) 结束可变参数获取.
* g' y1 }4 c% [! y+ y5 b
: h( e& C' ~, l+ k3 ]( E& T9 m3)可变参数的使用实例$ D# v& h$ S0 `$ n6 H
* u8 I  R9 {/ x' N8 X6 {# ?
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
% F+ ]* }$ b& f+ w& H: x7 j6 P) a( ]+ ]3 m$ z# t
#include<stdio.h>2 }9 H, u+ N0 K
#include<conio.h>4 Z( }! c& {8 o8 x/ Z% P
#include<stdarg.h>
% _! S0 b9 h8 A/ mvoid tVarArg(int num,...);/*num为可变参数的个数*/1 ]1 }" a% a' T6 B
int main(void)
% ?9 C+ f+ t, q, `! _0 d- V: f{1 e/ k( S" q) h& g( ]& D0 M0 F
clrscr();6 m. j" Y# O; `& |  {
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
- f% ?% \8 `0 q; v% ttVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
+ D) T1 e6 Z; X. B& }getch();
( }0 }& u) {; S1 t7 c/ i* ?return 0;
. I/ e/ |2 |& ]9 h' K}
( S3 ~1 P3 \0 q3 _6 Svoid tVarArg(int num,...)
+ A  z$ z3 e5 i' l, c2 n1 ?{* Y$ r( [7 K9 C
va_list argp;  /*定义一个指向可变参数的变量*/+ F* M9 U$ u4 ~* x7 d. T
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/. q1 A, f$ ]; M9 M# e$ `7 H$ [5 I# C
while(--num>=0); ^+ w$ D/ h9 p0 U, N) C
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,2 h6 l- a9 |6 d# w+ K  e+ \
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
: f* R5 ?* Z0 f3 G0 _va_end(argp);  /*结束可变参数获取*// Q. Z$ F# `0 T' K6 C
return ;
& H$ o/ U5 I1 Y$ T# s}2 j" J( X* v9 W

8 p% o& R3 P3 A0 z* Z2 a% o& W" n4)可变参数的使用需要注意的问题# r, O  B& x0 k$ R! @$ O

: N- d- i- H3 v/ e% M2 J1.每个函数的可变参数至多有一个.+ }* \' x; m  D. ^/ y4 A* m
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.% ~9 Y% O6 A+ @3 P) n/ x, l0 m' l
3.可变参数的个数不确定,完全由程序约定.
4 d" l( D8 ~: ^' Y7 G! ^* T! l+ [- b4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.& e. V5 R& V: M
而printf()中不是实现了识别参数吗?那是因为函数
1 m. }3 N/ D7 @1 n, a/ _2 v% E* lprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
1 `8 T* k1 V5 g- \/ a" {$ [: `, K的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 # D/ Z2 f/ ^8 k+ M2 I' F/ d
过在自己的程序里作判断来实现的.
0 o* j0 A; a: y0 X5 p5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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