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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
! l6 B2 F1 _! x8 _) `0 Z7 c6 F它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔. C( `1 s% l) e5 ~
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
i6 T9 m: W2 j: m2 K% V; A定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有: R1 P, h; B) I' r/ E/ C" x+ f
实际的名称与之相对应., R9 y. b. I9 D9 N) ^- P
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.+ K9 j. b: |! h D; B. ^, g( P
然而,更多地自由,同样也加大操作上的难度.
+ \/ ` E' H5 p( P1 H, P$ M# V以下就对可变参数的几个方面作一定的介绍.% G4 m" V! ]+ H* l* L0 m, J
8 }+ i; S1 A# u& i3 R
1)可变参数的存储形式.
8 x# b. T& k# v- D2 ~% b* k
- B: L" z5 d1 g$ B' L& O( n大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,1 W$ B! P2 O. r: |& r
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.- ~3 `5 w# v! A" X8 U
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
9 O4 F) D6 v9 v; {这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
+ M5 l& r) P1 @0 w& H8 b- N因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):' ], G6 i" c8 ^% B2 m% V
栈区:
( h, n/ H7 J. {; C+ _2 p7 {. E
7 B" e7 [# e' ]|栈顶 低地址1 \ J* x5 q, C& ?+ x; q
; s4 R* \1 y9 [" W5 I! t
|第一个固定参数var1
" P% G% I2 x; o! A8 f0 P|可变参数前的第一个固定参数var2, u7 b5 g% t& D1 n
|可变参数的第一个参数
' w& R8 ], U. K: ?. X2 A8 y|...
8 G2 N' _# w1 i& C% P|可变参数的最后一个参数 d/ h. `! M Q$ s0 F
|函数的倒数第二个固定参数var3
) G, ]- ?: V, p$ e- D5 [/ K# _|函数的最后一个固定参数var4
4 T; s7 \* D7 m' R: D7 U|...
- ?$ ^$ r: `7 l* B7 m|函数的返回地址5 |7 K) E2 m6 L0 Q; \) t
|...1 `/ B/ v$ E' @( v
|栈底 高地址
0 s' l) S/ c6 [& h; f# J2 [+ C2 b& C9 L x
2)使用可变参数所用到头文件和相关宏说明
. S' L' R. W& L7 b0 ]' m' M1 i6 M& c$ {; ^, |
在此,以TC2.0编译器为参考对象来说明.
( W% x' E+ i' A, M, ^7 v$ \9 l可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
( h: p- n1 p$ n: v: e: N: e# X( t% y此文件为:
" G5 b/ ~+ ]3 c/ r/* stdarg.h- K7 \" i, m- f* y9 E; @4 B
7 \! U) C$ B6 o X A+ Q4 n+ d7 X, Y
Definitions for ACCESSing parameters in functions that accept6 h7 P) H7 O# f& X
a variable number of arguments.
. i8 W9 X$ {: Q# {( q, }" e1 T( [7 |1 j; k
Copyright (c) Borland International 1987,1988) U j/ L& j; j$ T7 Z9 V8 F
All Rights Reserved.
1 ]( J: h6 P: d2 L I1 ?# W, a*/- E/ P3 v* d- R0 H1 S, o( y
#if __STDC__3 }2 V, d$ ^1 C
#define _Cdecl3 @0 ~) @4 B3 J# M H1 y
#else
2 i' u/ G7 m+ Q#define _Cdecl cdecl. b. N2 H% F; f. p2 D
#endif
8 O' D, `; [4 G$ W% N2 t" R/ h; v- s
9 p; r( m9 [6 _2 X' @#if !defined(__STDARG)
) G5 n$ ^! H: ~4 F5 q! D#define __STDARG
- Y+ E1 d% Q1 Y% \4 k
1 j r4 `% q$ N2 Ltypedef void *va_list;; a1 |+ t: ?3 s$ t' B9 V& g
- m" S( J& w/ F# C" @1 @9 H0 q
#define va_start(ap, parmN) (ap = ...)
2 {9 A1 @, R% c2 W# @( V#define va_arg(ap, type) (*((type *)(ap))++)
' w; Q& S; @+ N# s( O#define va_end(ap)2 M6 M+ i8 ?% B9 B
#define _va_ptr (...)) V6 ?4 Q# F' v. G% ^4 K
#endif/ w) `3 b' @! h& N/ t
" c( I- h% [ V1 ~% {
以上为"STDARG.H"的内容.
( i; ^( v$ ?3 j4 w& c% r: u该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
% t; a5 T( U0 ova_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list, N' v- m5 R% }) c) u
parmN为可变参数的前面一个固定参数.; d4 q2 }9 G& C" c, S0 ~
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
Y0 A& V- D: E e- d5 T! v/ Y+ @ yva_end(ap) 结束可变参数获取.6 G; O1 h4 @( y! J7 T" i
4 N2 {# _, O; s! J3)可变参数的使用实例
# k1 N2 `( T0 Z+ G! ~3 k* L- e# `0 T! @* q
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.& v9 j$ q; ? P- O; Q' o
' V1 {& M4 {9 g
#include<stdio.h>4 p* g/ p( N% n/ R# Y: W4 Q
#include<conio.h>1 h! G5 M) \, P+ i3 z
#include<stdarg.h>
5 N7 @! U3 e5 j5 H: Hvoid tVarArg(int num,...);/*num为可变参数的个数*/
8 _- {! _ ~/ K( P+ @. V- ]. Lint main(void)! P) \9 I5 t. n: } G5 |
{
) P5 X3 G: t' j" R$ w3 Rclrscr();% J# a% ^$ t6 l& K, d7 W
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");8 S6 C$ S+ A4 a, u: Q' e+ w
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
' V0 u$ a: C' f* h7 lgetch();+ A7 j0 \& k& q: X- v, m7 X
return 0;* Q5 b, E9 V W$ r
}
( m$ z* V! T: }void tVarArg(int num,...)7 f* h6 B" |5 _8 ~7 q- _
{
+ p9 S! g! d, ^9 {va_list argp; /*定义一个指向可变参数的变量*/
* H5 H0 C' O# Jva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
/ C3 [, e* p, n6 Pwhile(--num>=0)
+ V" A/ p' o {) o) t printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
9 q% ~6 S9 J; i4 z6 g% C/ O 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
( ]1 J$ b% s. h: z# [. @) z5 Jva_end(argp); /*结束可变参数获取*/* Y2 }( D! [9 b
return ;4 c2 p. p; K' q1 r
}
6 w+ g& A: P7 |& R+ S W9 f7 I8 q' H% K: E+ D; @9 L' ?! t
4)可变参数的使用需要注意的问题) c3 |, m& K( ^9 Q
$ r0 f1 |; N+ K" F k1 W& I
1.每个函数的可变参数至多有一个.! ]/ R/ `& d# q; C: J4 v7 M
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
9 |; g/ k5 ^' S) o2 v3.可变参数的个数不确定,完全由程序约定./ C; @& {, S0 N0 q
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
# ]+ k# D% w# x, q: B$ m4 ^ w: G而printf()中不是实现了识别参数吗?那是因为函数 & C! `, a+ [/ O+ H
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
( h% O- @& d7 H, d& n" l* a; S! l的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 7 Z7 |# V% L5 X! C
过在自己的程序里作判断来实现的.
, S, J+ \- g- [* H6 s9 m5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|