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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
2 e% n5 U- x: d( _. a它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
' F1 n) Y# F( ?' c% a. {2 o可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不. J/ L$ }; E: d7 d$ ~6 A
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有$ ^6 J; `: d! h# D
实际的名称与之相对应. e% O$ |: h! S4 s
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.& A( i ]) e6 F' b
然而,更多地自由,同样也加大操作上的难度.
* K* {* \* e k3 `: v' ?; [以下就对可变参数的几个方面作一定的介绍.1 m. T' j B9 D, I! n
9 Z9 ?4 }5 m* m( P! j5 o8 G1)可变参数的存储形式.; w3 P, @" ]1 p7 |" G- e# t
Y: i2 u' U0 _- ]# o大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放, g0 N, Z0 V0 h
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
, w1 j! H/ e7 m7 _ @' z' S在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,6 b" i: _5 I2 y7 m: m' B5 u/ @3 ^) a
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
# F+ z5 A; f: ?- g! z& J% o2 [因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):* k+ R N# j7 T5 m5 {4 X# u2 m8 [4 A
栈区:
2 b7 @: B/ H* V4 N/ L6 z- ?$ T' h& V! P" F
|栈顶 低地址
$ G# k6 i6 V) I# J9 ^, ]( U( U6 e( O3 K: b! B
|第一个固定参数var1
+ E" t; a, q! m* o|可变参数前的第一个固定参数var2% J' J# W% e/ g9 k4 h+ `! A: W
|可变参数的第一个参数
* ^% ^7 B1 i# T+ a& h+ Q; ]. q|...
& X) d' X6 |' a|可变参数的最后一个参数
. E2 B+ L% G% b4 E7 p7 z( W|函数的倒数第二个固定参数var3
, N" } Q* _6 W7 t% v|函数的最后一个固定参数var4; d4 d0 Z2 D9 u
|...; j4 ^$ q" C" j" x; x4 ?
|函数的返回地址$ o: ~( C1 d1 `. @/ \) S- v
|...
2 m1 O8 O* f8 ||栈底 高地址) L9 R9 Y+ o7 d$ r( {8 s! N
) [& d+ M. X# t& U1 n3 z. g2)使用可变参数所用到头文件和相关宏说明' b& u Y7 }* P; G8 ^
U! i: }: ?# L在此,以TC2.0编译器为参考对象来说明.1 B5 r" X8 R; O/ t& E6 @% S
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.' |: D- P( d2 g" E
此文件为:" }7 Y, ~0 G9 m, N* H0 Z' a7 Q" k
/* stdarg.h8 R- L0 I4 a& E3 k
0 S7 h! n+ O& L5 y# W# R; W9 dDefinitions for ACCESSing parameters in functions that accept* |5 E5 X7 d U
a variable number of arguments." \3 u3 M9 I1 b# Y/ F
: C' n! E7 U8 x1 \2 Z- b
Copyright (c) Borland International 1987,1988
) i" Z1 ]8 `1 P$ b) oAll Rights Reserved.+ A& }* l9 g5 V* ~! U) t
*/
7 q t4 D; O6 F z9 y q a#if __STDC__
9 ]# w( V# w4 {, y#define _Cdecl N q# P6 }7 _2 x! V) b( O
#else- P! V o6 G5 c# b& a
#define _Cdecl cdecl
( n8 o2 Q1 h( l0 _' o: N#endif
' V/ @ |9 m) P: w( J
5 k Y3 d: d( u, S/ b2 V#if !defined(__STDARG)
: Q) m. C1 M l#define __STDARG
- k0 I! j* J$ n- d+ l& ?2 t' b, C( E8 A/ U; C
typedef void *va_list;# t6 A" X2 v) Q- r# U8 l' j
) U- L2 Z( Z6 ^4 C6 N
#define va_start(ap, parmN) (ap = ...)0 j1 E f' t4 E% |
#define va_arg(ap, type) (*((type *)(ap))++)# F) k! E# ]) R7 |+ S
#define va_end(ap)* l! f! C( T$ X& d1 e! n" a
#define _va_ptr (...)
% b3 i/ w' ^( C; M* O. C# B) \#endif8 @8 i& k. i/ j. Q
$ G2 U' Z# T) R# `以上为"STDARG.H"的内容.9 a8 g3 P( Y: E
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;) ?/ E( J+ e+ h6 o
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
) Z5 X* K/ }6 ` V& UparmN为可变参数的前面一个固定参数.
2 A/ T$ @- L0 H4 Qva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
) g) g# u( \8 V5 Z$ r* W; Yva_end(ap) 结束可变参数获取.- `, b9 j" o8 C6 [8 X
& N9 E$ c1 J# c2 ~! g) o" u* V3)可变参数的使用实例2 W, K# M1 F# J" P$ ^8 P5 T }
0 J) s# B8 W; l+ A! I7 l5 j实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.: r' U" g0 g- i6 Z n0 r) C
9 ]7 w; g" c& p" `! {
#include<stdio.h>
c+ k% ]! B2 L p! m* i#include<conio.h>6 D- ?) @. x* G5 s) x) R L
#include<stdarg.h>; \) w3 H; R9 c
void tVarArg(int num,...);/*num为可变参数的个数*/. \/ [3 @9 L H9 B; S
int main(void)
7 ?! p7 G: _) k* z/ c4 s{8 J7 b# k3 o w/ z, G) c
clrscr();
: i3 W: f- w( X5 I) O/ f% KtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");" V3 j# `9 ~" g6 P
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");3 q" h, X9 q2 k
getch();
' b/ v. z3 ?4 t& preturn 0;0 J7 c9 [8 G Q
}
5 ^) ?! ?0 W. E. y) F1 Zvoid tVarArg(int num,...)# L k6 h8 K4 r! z# h
{
4 J7 k7 \' b& ?9 }" P6 ]3 jva_list argp; /*定义一个指向可变参数的变量*/0 F. L5 B4 L) d" P
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
. j3 J) y+ ?& e; i0 S; B' Jwhile(--num>=0)" s: w' N \7 K8 l2 j* d$ @
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
* G! d+ o0 ?4 W; u$ a& ? 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/, M, } Y9 x$ j: }' k% U4 _! A+ T
va_end(argp); /*结束可变参数获取*/
: ]6 l; A+ |5 i$ O% Rreturn ;
$ T3 l- W: D4 V' d7 T}' ^9 [7 X$ H1 U* k- \( |1 j
; }+ K- v8 c1 n; N
4)可变参数的使用需要注意的问题
6 r1 D4 i# E$ H3 {0 B0 y; K# ^4 c
1.每个函数的可变参数至多有一个.
. n- u/ K0 H, X7 M4 b3 f S2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.& u- S' s% e6 B5 x" i- U
3.可变参数的个数不确定,完全由程序约定.
' Q& x; K @. q1 @& g* ^8 c4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.) R! \: m3 R) y" ]& B) d4 S
而printf()中不是实现了识别参数吗?那是因为函数
0 p5 ?8 k7 x9 [1 X Aprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg + P" ]3 G! i) l
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
* c1 l; y# h3 s2 q过在自己的程序里作判断来实现的.
$ ]& ^( A8 z4 }5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|