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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.0 s n3 Q: \1 @; X0 o. R
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.7 q' P% u$ T$ W
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不+ J4 Z; r2 o+ L( x* j1 `6 X
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
7 ~8 U' K9 u7 T J4 v- M实际的名称与之相对应.
0 v0 G. v; a3 E由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间./ }: Q- A/ y( I/ i7 D& I9 R
然而,更多地自由,同样也加大操作上的难度.
+ J$ `0 F" R! p! i R1 L. @以下就对可变参数的几个方面作一定的介绍.
6 k2 v1 l" p, O* ~. u6 i; m
* }! ` E8 Z; {, a1)可变参数的存储形式.9 R' p6 k/ ^0 X1 g% P" h# F; [
; g$ n4 h* X% G* f* H; |
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,1 D7 r3 ]. I: E2 }" t; o
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
% R6 H$ @ T# ]! O# c在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
. j0 e+ F1 }* z; w% Y这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
' I4 s/ F7 o7 N( }6 A; V" ~因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例): z2 k) Y% s' ?. ~
栈区:! F( U0 n8 ^7 e4 h5 f
, v" Q4 ]2 j+ F- T7 C H. U
|栈顶 低地址+ x/ V& Y( D4 s- M3 u8 \$ m
5 m8 A; h9 _* o) i) K|第一个固定参数var17 T; W7 W T F, K- h1 e( h
|可变参数前的第一个固定参数var2* @! T6 g( Y% h/ F
|可变参数的第一个参数
" |/ a6 a- \2 l" X$ F7 m" D|...6 r, F; n. w0 b9 ^8 b# ~+ w, m
|可变参数的最后一个参数
; }9 s; h- K8 f/ ~- @+ ||函数的倒数第二个固定参数var3
$ ?; V: c- @+ t6 l" y|函数的最后一个固定参数var4
4 S, I/ T4 ^) X- W( @: A+ |6 E5 r|.... n. }- w# X! d; n9 J0 o. m
|函数的返回地址
( p& J2 q$ x' z|...
' l" F! O1 x. y5 A|栈底 高地址) b. M9 Z9 _) [% F
; q- M: D; N2 _7 j, F- R
2)使用可变参数所用到头文件和相关宏说明
; j) F- D* f7 M* b3 Y! S& L' G: q$ k% S, a O ]- |9 c$ ?
在此,以TC2.0编译器为参考对象来说明.
1 B0 e+ C+ w* d7 I V% w可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
; Z; n/ B0 r$ A& x7 G此文件为:8 Y5 z1 c% W1 ]: Z) r
/* stdarg.h
8 ~( d. x6 X# A# F" A8 v
' l' E q( k& z* ]7 A* Z9 Q9 X p/ [1 yDefinitions for ACCESSing parameters in functions that accept" _- u% C `' X! q# W% w. c! `0 P
a variable number of arguments." @( l9 a; L1 C+ l& |! W6 |! I
3 q& ]' H" g3 x7 O4 X$ B0 lCopyright (c) Borland International 1987,1988
# \- d% Z1 Q: t7 _All Rights Reserved.. h f# Y* q/ R, V& p3 v
*/
- z5 m9 R$ R: {- e#if __STDC__' b: H0 _' u) |; ?) c
#define _Cdecl
) P9 v2 q3 R' s! j) |5 m# [#else" v l: r/ r9 a' v" s; ?
#define _Cdecl cdecl5 C" G! L1 P: k: Z
#endif
" g7 G; w# w* s
6 o5 G" H* x i p#if !defined(__STDARG)
, R4 O7 S) `' M: V#define __STDARG
- U% W3 _- Z8 K, ?3 D) b6 v4 b! j4 G3 o9 G6 j
typedef void *va_list;
, C- n; v; E" T$ J! m$ S9 r5 f+ s# G5 ~* I
#define va_start(ap, parmN) (ap = ...)
5 t# B; J# v- ~/ a( \#define va_arg(ap, type) (*((type *)(ap))++)2 E, c5 O$ R3 S [5 X% o* E
#define va_end(ap)/ C0 F5 f& V% F
#define _va_ptr (...)
|7 v8 o9 {8 X7 L! W#endif
- Q! a% s8 ]4 e- ?& ]
1 L+ Y" z5 G0 E以上为"STDARG.H"的内容.
' e4 [4 ]- G: k+ `5 p" \. M$ j* L该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
- Q' T6 v0 H( Rva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,: m" B% A! \: \3 q' f8 L1 N
parmN为可变参数的前面一个固定参数.5 Y) p) Z; j8 K8 c2 J9 A
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
. U' N6 ^; \9 K7 ?$ hva_end(ap) 结束可变参数获取.7 b$ ~7 v% b9 j& S m- x
4 z0 s8 |2 J5 a
3)可变参数的使用实例 w, y P+ v& b/ N* W; r
3 Y, _% _% r0 _# v( c9 y; X
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.- r& s2 V: c( r
- I. |/ b# U* t8 q: D#include<stdio.h>% v8 n% V6 Z( L( }# k$ S
#include<conio.h>+ g1 l' `0 z( ?9 Y1 Y+ t
#include<stdarg.h>: f( g: u5 S2 H) Q1 ^% H
void tVarArg(int num,...);/*num为可变参数的个数*/5 H( {& e3 l% a9 d( F" l& v
int main(void). f: O# t2 k: ~5 b5 y$ N
{ @+ U- a% i4 o& [* d
clrscr();
4 ?, M3 Y+ N; q0 j$ u3 g. |tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");7 _* Z( w: Q0 M/ D6 S8 x) d! H- f" h2 O
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
, M( z; X+ w+ M) D$ C% H( T) Igetch();
' A3 A/ I* ?* l1 O; Xreturn 0;3 E+ q3 k2 q% F
}( B/ @8 M* @/ V4 r( ?3 Y
void tVarArg(int num,...): y( N* R1 @! z7 ^+ [
{
' ?& Q+ N; h& `' w" M8 Lva_list argp; /*定义一个指向可变参数的变量*/ v; i. Z# S9 |+ P( ?! x$ I
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/+ }# R4 t0 M! F/ d5 ~
while(--num>=0)
' B: H4 R: U& U ^ printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,1 Q* ?, p( B. X3 p
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/" ^- O0 x/ O! Z3 s8 s
va_end(argp); /*结束可变参数获取*/0 s5 i( Q1 J7 h. K* O' B2 Y4 ?
return ;
" a9 B& M+ j7 q# x, a7 j}/ e, \; m5 j$ U0 g V( i, a# R
' ]( r4 g5 A0 z# f% H! G
4)可变参数的使用需要注意的问题5 Q7 ]' ^ y2 N5 W' [5 {: g+ |* Y
: c! V6 K4 | S% U1.每个函数的可变参数至多有一个.' b) i c) B: w$ V' _
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.. a- W. z2 a( Q& T. l# J. A2 I# ]4 q
3.可变参数的个数不确定,完全由程序约定.3 K# ]: d( b; w$ F; Q+ b
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.% o+ U& ~5 d1 l3 H: Y0 D' s) b
而printf()中不是实现了识别参数吗?那是因为函数
8 u6 ^% x' B2 W+ f* H5 wprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg ) j2 s, w0 `; I' o! z
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 ' q9 l, w- l* D4 t" v
过在自己的程序里作判断来实现的. $ V: r U$ C& H l8 A& n) O
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|