  
- UID
- 133
- 帖子
- 51
- 精华
- 1
- 积分
- 186
- 金币
- 55
- 威望
- 2
- 贡献
- 0

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
: X; X$ `+ `) T& K它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.* z& o' w3 ~; E, ~ f& k0 I7 c. q
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不9 [% L, L8 \9 g* d q# q
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
$ P* ~9 u! d6 }实际的名称与之相对应.
# c0 Q8 m* `# D& G0 Q& o$ _! p) [由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
2 k2 i, O( h3 V0 ` s3 ~然而,更多地自由,同样也加大操作上的难度.
- w1 v1 `" n( k. r& P以下就对可变参数的几个方面作一定的介绍.
0 ~7 X; ]6 K$ `" K7 S& }- k
% D/ a+ H. V8 S' r- Y' n7 @1)可变参数的存储形式.
# p1 K9 V1 e- Z9 d
' \* W: t! ]8 {# W- j8 R8 ^$ u大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,* ^% b2 P; [" s p
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.( w( i$ c: p- r1 O
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
$ w) [5 x/ Y' o这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
* e+ q5 f, n: r& @. p7 w1 h: f因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):! b. R$ R( ]5 K2 `- C
栈区:
3 M8 J( d6 m7 S/ x3 @( m1 ^3 v$ f$ q( \* w
|栈顶 低地址& U8 r" P! p) Z2 }% [4 v
5 ^' s5 M8 n! t- P& X( U|第一个固定参数var14 W) a$ w9 Y- Y; f8 }
|可变参数前的第一个固定参数var28 X- x9 [( e9 ^ Y
|可变参数的第一个参数
; E/ P0 ^' M& R$ a$ [|...
- r1 P3 C& Y6 T. x|可变参数的最后一个参数
, @& x- n. G( e2 Y I& n5 u# c|函数的倒数第二个固定参数var3# v& O8 } }6 O$ r& D0 Z7 _- r
|函数的最后一个固定参数var4
+ @7 w) B2 c: k% ~|...
% r3 o9 D4 ~' F2 y|函数的返回地址
( B. B& Y6 H, I+ C$ l2 \2 i8 g; B|...
: R" S* p- u9 b: }% x+ Y|栈底 高地址9 o; l: c: \- h, ?
- Z/ h) D% y* X) t; V2)使用可变参数所用到头文件和相关宏说明
! f! E, e9 y2 D# p; J+ W1 {% Y L ^! n2 K. N! L" S
在此,以TC2.0编译器为参考对象来说明.* V. p4 H: w1 _7 [5 z7 q+ Z; Y
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
, J( s1 N1 A! \此文件为:* d# P6 w4 B/ K; I0 U
/* stdarg.h
1 K) k4 W- u* R
4 g6 M* y" b0 g- T. w8 YDefinitions for ACCESSing parameters in functions that accept
# e4 c; _6 \* g8 Da variable number of arguments./ w6 E, ?4 q; \3 m! N' c; C5 R9 h, _
j7 ^0 p+ h) {* C2 N; O2 ?Copyright (c) Borland International 1987,1988
0 A) w X# C# q7 r+ ^$ i8 qAll Rights Reserved.
; m' N& b, _* X* R, `9 d*/
4 c" a8 E v8 {, \7 m: s#if __STDC__+ Z$ N# D3 [" z: Z) A
#define _Cdecl: i1 {. f3 ]' V+ O
#else6 u$ d' W( d( B: u0 b" x1 s
#define _Cdecl cdecl
1 _$ D. c' w: @1 V2 Q+ d#endif! c$ r; ?' L8 |) `. Z
4 s; i9 V4 }+ R; X$ J' Q' K/ I, D5 x#if !defined(__STDARG)- m8 s" s9 t' H7 e
#define __STDARG# r9 D( B& B$ ~- j0 n
/ `- V9 o- G b- |! m) otypedef void *va_list;/ M2 C2 a5 N* F3 Q, v% Z
" J5 s( | \) }$ y1 G! m#define va_start(ap, parmN) (ap = ...)
: G& _0 J4 Q) h' q#define va_arg(ap, type) (*((type *)(ap))++), u8 s# ^ i& {4 @
#define va_end(ap)/ M4 h' v* ?1 K6 j2 h- b
#define _va_ptr (...)
- `' c% M3 p& {" r#endif7 J! _% s" @5 ^; r- V: b. y
- N7 C1 w) o8 b D9 k
以上为"STDARG.H"的内容.
( A S1 L$ \; X! K, r" J该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
+ x% ~ E; A) }va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,9 o3 U$ E: u, S/ w$ C
parmN为可变参数的前面一个固定参数. }4 X0 @! e: Y; `* |+ }
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.# \- L3 v. l* L6 S; D3 J a2 e
va_end(ap) 结束可变参数获取.
* G8 o9 y7 [& i; \* O; B9 N6 J @+ J( p. i
3)可变参数的使用实例
7 `* Q# j, q4 I2 D$ u4 M
' O- o. E4 ]" {' V( @实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
* N h3 o$ z) o/ r$ q% F
0 E8 U' Z7 _2 V- K9 G. V+ L* x#include<stdio.h>
& }4 L% S* z3 R2 d7 B' f#include<conio.h>
6 f8 Q" g+ F# q4 x#include<stdarg.h>+ L) N4 |! x1 `- Z* x/ f
void tVarArg(int num,...);/*num为可变参数的个数*/1 [9 B# f+ L3 ?6 r& Q# B. J7 m n
int main(void)) L$ G/ d% s% T% l' B6 B$ B
{
, q6 G( O9 u& F% K5 A6 W/ N/ Rclrscr();
4 f8 Y. p8 ?/ j: B# stVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");3 |6 H9 I$ T7 |+ z
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");% `# h$ F4 y) n( v1 v
getch();0 D' s* ?5 g) o. [/ b3 n
return 0;
" P8 C% u3 @6 M2 [. }; X9 x+ H# h& U# A}( @3 G5 a9 S0 l& L
void tVarArg(int num,...): m. h# ~4 {4 {! v- l. z6 ?- R& |! C, s
{, D% r" D0 [* M; M
va_list argp; /*定义一个指向可变参数的变量*/- a! F9 w, l0 e! d0 m
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
& \# M2 Q; H/ fwhile(--num>=0)
6 U# I# Z# O* e" ?( o printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
7 k7 c& R8 J9 ]6 }+ ?) Q4 m5 I( i 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/5 h$ ?5 C' _8 t+ O6 A9 x" N
va_end(argp); /*结束可变参数获取*/) D7 W7 G$ E' m) U
return ;
9 M1 I$ a: y; p( P% L1 V1 u}: |, t% q2 i( A* L
5 K% x e& F8 M5 f# R7 {2 C4)可变参数的使用需要注意的问题
& c& f4 Q0 r1 [' b. x# j9 f n
1 Q+ u5 P4 V$ N/ G0 f+ o1.每个函数的可变参数至多有一个.- V3 ]. C$ C+ F m I
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.5 i& ?7 u/ K4 y' P* U
3.可变参数的个数不确定,完全由程序约定.+ q. |, e/ d: \
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
3 ? d) v$ \$ E1 I* C- I% T而printf()中不是实现了识别参数吗?那是因为函数 1 ~+ G7 c& ]$ P/ U1 G
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
3 H- ^: N, a4 ^' R" ~的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
0 g# u. Z7 b$ k* W' ?过在自己的程序里作判断来实现的. & u* |8 h2 s: ^! y, s
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|