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

函数的可变参数详谈

可变参数的英文表示为:variable argument.: B. S- A0 R( U7 o8 J' x6 o
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
: W7 M/ u$ A$ d7 }% l0 A; Q可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不4 ~) T+ L4 g" e( r$ R9 m+ ]& T/ q" e
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
0 y* Q; Q* D  w实际的名称与之相对应.
+ C) }: L2 R7 ]; f由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.8 u& b; k4 S5 ?$ Y  O
然而,更多地自由,同样也加大操作上的难度.0 E" _) x2 E* ~
以下就对可变参数的几个方面作一定的介绍.
. T7 m4 }6 E) N8 Z. B2 \
8 E+ O/ R9 d1 A& w1)可变参数的存储形式.
4 z" |% u, w) r. _. i" N* @& b' {* d2 A- C+ B
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
) w0 r6 M5 ~2 h* p, f6 i存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.4 F$ O, G7 X3 B" H6 s" p% [. ~
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
- c8 ?# W9 X* Z: _, \这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.4 Y, p9 y1 E' e' p
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
3 v7 ^+ V, L' c" M, K  @% E栈区:
& y9 ~' e5 q4 L6 c& Y2 [( O) d. t; V3 n2 D6 O6 c: V
|栈顶             低地址1 e: z) S  x% w' v$ ]+ G
2 a, h- n- `' A: [) ~9 b# B
|第一个固定参数var18 Q; c: c9 \: M: B8 S3 u
|可变参数前的第一个固定参数var2" s$ `) k7 N: w9 E7 Q0 w
|可变参数的第一个参数2 t$ m! M" |' ^) |
|...( r! ^& L; I5 {3 Z* Z- u& b
|可变参数的最后一个参数( r! h3 s2 K1 y. j7 ]) e$ M
|函数的倒数第二个固定参数var3
% v$ {% f& ]) n( N5 c; M; Q|函数的最后一个固定参数var4
* o' _# G  n: R5 U9 s|...4 {; j, v% [# A9 ?6 i+ x
|函数的返回地址
2 V  v$ N* ^1 F7 ^9 ?7 N# G6 j|...
2 h# R1 k9 k  B% `' `|栈底    高地址/ x) }" b& g  b( D9 }5 G

. T$ w4 F" v$ s2)使用可变参数所用到头文件和相关宏说明
; m6 u% e3 L$ c2 W& n
3 F; C& y; W  j, C5 C( R+ l  ~* ]在此,以TC2.0编译器为参考对象来说明.2 R* T! a9 y0 W" `" k
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.' C5 n8 K) F) v( Q5 n9 v
此文件为:
$ u, f7 {  y. {" J. t/* stdarg.h5 Y6 E2 q# j' q0 f7 M
$ l/ U) r$ J2 {
Definitions for ACCESSing parameters in functions that accept
7 ]- s6 y$ d- H- P# H/ w+ f# _a variable number of arguments.
7 R7 C* i* Z% C" [6 O
1 s) H# M% p2 q& x9 E1 E3 H$ G1 wCopyright (c) Borland International 1987,1988
: f6 @3 c% q: T( fAll Rights Reserved.$ d! [# }7 X' M  ]) u" Y
*/) O4 L! e6 `1 y5 d) `; ?/ j  J2 ]
#if __STDC__( v9 |+ l) V3 a/ H' R) e3 N
#define _Cdecl1 g# N0 U6 W, U2 Z* p; A
#else3 d9 B8 x5 g. O( Y0 S
#define _Cdecl cdecl* {0 a  a0 F" E2 z! w2 Q
#endif
. j7 }9 }2 a% r0 A# ]
% W; O) N, B4 i, r0 c% q0 g0 }$ b/ j#if !defined(__STDARG)
7 Q1 g' }6 r+ y* p: }+ a7 M9 x( `#define __STDARG9 l; R6 s* C3 k9 o
2 v$ k3 v5 D' f5 h. o
typedef void *va_list;: a2 L$ F) `! h& j

7 a  I( M  c- |#define va_start(ap, parmN) (ap = ...)
) G, P6 W, L" y' L0 _  M#define va_arg(ap, type) (*((type *)(ap))++)2 K& q& W, b4 Y' a1 E
#define va_end(ap)* U$ v) _/ Z" m; L! }
#define _va_ptr   (...)- m$ j9 {; v3 Z% v
#endif) a8 G9 F, \- f' f$ a- n) f

+ U6 p& F7 Z8 B以上为"STDARG.H"的内容.! I( \  c/ B3 \: M. Q
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;. M/ t8 K* V& T9 x6 W$ L; i  }
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
2 _$ Y! h/ z. _' p5 [parmN为可变参数的前面一个固定参数.2 _+ e- @" P9 O" l: ]: O
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
2 W, b7 w& z; Eva_end(ap) 结束可变参数获取./ D! [, C" M4 _/ ^. S
% m  x4 w: m# M: A1 v! ^
3)可变参数的使用实例
9 V* J/ J% O' K$ `: b" n9 \8 c2 X) c: Z3 \- X3 @
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.& p- n: m9 x$ Y4 S5 g2 c: R, {4 N( d
" l5 Q; U7 c. D1 ^! b* J) Y
#include<stdio.h>
5 m; h# C7 C- W0 F6 d6 Y#include<conio.h>" g* K3 O% K* M$ e0 B: h( H
#include<stdarg.h>1 V5 r# W* u8 h" S# H+ o; x3 @
void tVarArg(int num,...);/*num为可变参数的个数*/6 `. G! d+ K9 h8 n- u' A- X
int main(void), C7 Z2 ^& }/ I1 w2 o& J: _' u! a
{8 o) ]4 x0 Z( g: l
clrscr();. P# Q" S! j) ^! ]# e
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");, v, T% m4 g/ ?2 @$ P, k. t
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
' A( d% j; W2 E! E7 ggetch();
5 T! n* v, q2 l* creturn 0;
: d; V9 w5 J" A% _: x}
0 T. I5 B, p8 c1 u* ?& \void tVarArg(int num,...)
% H2 Y3 D+ ]0 o: w( A" F{
5 T9 {( |/ _8 Mva_list argp;  /*定义一个指向可变参数的变量*/
5 a/ e9 ~5 D5 n6 G+ zva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/) K! v- `* U& \1 _# `9 ?
while(--num>=0)' i8 }- H+ W% {# ]8 d5 C
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,' |& M. c$ k& G% d+ f2 S
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
: F7 [2 r( K1 I# N. I7 Uva_end(argp);  /*结束可变参数获取*/
* |9 S3 t% y; s' ]! ^return ;
3 U5 b- K3 D# o3 G& {: Y}
8 F0 r9 j. k/ ~0 ^' ^* Q7 c; D1 _# f: T, |) w9 ]0 \# Z- [2 g- d  `
4)可变参数的使用需要注意的问题
* L( F4 T$ T7 O% M" g# c1 h, O
1 y+ {; n- X: W1.每个函数的可变参数至多有一个., I6 d; Z( \/ A/ ]; ?" g* p
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
% T% c( s; }% J7 s' e5 l1 V& E3.可变参数的个数不确定,完全由程序约定.
; s5 }. x4 ^) k# [% h8 Z, s4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
* N+ L; K) A! R% k4 G而printf()中不是实现了识别参数吗?那是因为函数 ( p3 J; r" E4 u8 U0 s- J; l
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg # L0 t* R- D0 @1 x
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
* Y2 }9 ]# Y% }0 M过在自己的程序里作判断来实现的. % O* }; f0 u& q# I: D: i+ |
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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