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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
/ d+ x! Z0 ]! ?9 n. v它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔./ ?! E: C, F# [" v! M! Q& _3 ]
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
0 K3 r2 A5 v5 z! j, M; M定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
1 v: w) d# A( m实际的名称与之相对应.# l. P' j8 _& m5 A" ^
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
% c. `9 P5 u2 o0 V V& G7 W/ ~2 `" o/ F然而,更多地自由,同样也加大操作上的难度.. L8 L" `# I, {9 c2 e) h
以下就对可变参数的几个方面作一定的介绍.
8 C) @' a) G! |1 X- @# Y( q0 R
1)可变参数的存储形式.
) W0 ^- f5 N. @
2 U1 [- X6 X$ y大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
8 p) b' [1 g; j- }0 B) }/ ?存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区., A: N* t0 @, s1 l! m! s. Z
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
1 e; a9 |1 b: A) f9 q7 z' h这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
" g) u. Y$ }2 h' w$ R" y9 [6 u因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例): W, P- i% I. e; Y E2 K4 F9 B
栈区:6 R- P6 F9 g/ J
8 ~+ ?- f% t* U
|栈顶 低地址' d0 y- J# @/ |2 K! u3 P
" i$ W1 L! Q* N: v2 w/ M# B|第一个固定参数var1! ]0 P" U0 n) Z |
|可变参数前的第一个固定参数var2
, |: q# P4 N( ?! ^* Y|可变参数的第一个参数- G% Q2 c: J3 Q ^! E% h( J0 o
|...) V; S8 l2 r6 \
|可变参数的最后一个参数
8 ?, v5 S x i" u|函数的倒数第二个固定参数var3
7 E2 |7 G2 z4 v, x' T+ T5 d8 D. W: ^|函数的最后一个固定参数var4
" X" S3 L1 x$ O( U, }7 w|...% v% @/ d. v$ d# }! r1 i8 C( G
|函数的返回地址7 a( N/ l& L7 x! b2 o5 f0 i
|.... F! g6 O9 a! A# T8 j- w+ X
|栈底 高地址5 W7 i2 `) N$ g
7 P( X) ~7 ?( n3 _
2)使用可变参数所用到头文件和相关宏说明. U4 \2 L E6 u6 X. r. A1 K( D- R
; g# \: {6 Y v- p
在此,以TC2.0编译器为参考对象来说明.
+ u: Q! Y7 s0 @ z可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
0 L( |0 U7 g7 {/ b此文件为:5 t* ^1 X) P& K' Q' P# H7 d* s, w
/* stdarg.h0 k+ m: ^3 p8 V/ q6 i8 o z! i7 c+ w
" g5 T$ v3 c( p5 G! v
Definitions for ACCESSing parameters in functions that accept
6 t4 R/ ]4 }# _: ~a variable number of arguments.$ S$ d' A) X- n+ T1 t
$ p: t3 ~7 z$ G8 N+ P# h& h, ~Copyright (c) Borland International 1987,19887 k* R0 R' G" ^4 d# P
All Rights Reserved.* b& I/ }8 |& H* m- _
*/
& m6 k; [) T6 O1 [3 w#if __STDC__* z- V _1 Q5 x
#define _Cdecl6 D5 A: D6 y. n1 m/ o$ m
#else, o9 F" E! q& [! {
#define _Cdecl cdecl
/ v* M" g$ D0 M6 C( _2 ^#endif
6 E3 ~8 X4 {4 b- P2 _$ o
. r7 Z1 w0 ` Z* R, G#if !defined(__STDARG)5 H" t( |. w, P1 ?4 W9 f, S/ F) ?1 Y
#define __STDARG/ u A- K$ P7 m0 ?
. Y" t6 `, C+ p# N2 h' r
typedef void *va_list;
3 Q) h2 N X I* w& t8 z" ~6 e) `+ `) |+ p* B# o
#define va_start(ap, parmN) (ap = ...)
8 j/ k* k9 I+ ^* ]4 q7 n#define va_arg(ap, type) (*((type *)(ap))++)( L% ~; I5 _% N# }4 ]- {% G
#define va_end(ap)
1 w* t$ J3 v& h+ G#define _va_ptr (...)
7 X% d+ F& m( O' Q! F! X' ^#endif
/ o9 l* T |6 m6 ?4 r: I/ d0 b/ U, J6 {# T% a) A8 d
以上为"STDARG.H"的内容.
) O1 d9 q& a$ P1 s0 C. G1 C该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;9 p0 t0 ^0 O3 W8 P& j
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,' H) y& t0 M8 t7 f1 n( c
parmN为可变参数的前面一个固定参数.
& {3 q) U8 t+ W$ B/ c9 q- tva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型./ c4 w8 }. o- r0 q
va_end(ap) 结束可变参数获取.9 M/ p7 Y' ^, b5 J. J
0 y) }4 w) W' N8 h% H3)可变参数的使用实例
/ ^! G3 R/ M. @9 W8 v3 t. p! F! ?4 O' C
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.9 s/ i0 _+ j$ X9 g
* J6 `' N/ R1 p. y; R" C# F" O1 j#include<stdio.h>
0 a/ k" g4 b5 o, R+ x1 @#include<conio.h>4 s1 b7 S( u! ^% s; N3 N
#include<stdarg.h>
' h5 ]" G) I- K* P1 w& a9 Svoid tVarArg(int num,...);/*num为可变参数的个数*/+ K& ?# e, Z, l( i
int main(void)
8 E; Y7 X/ J( f2 e6 o- M{
g7 T1 S. e. X: {& Qclrscr();3 K4 _, O% J8 C* }/ k
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
0 z- e2 t$ F6 K% ^; EtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");, N8 p _7 a0 r5 e* ~1 U
getch();( c8 U' z/ l: S! t X' w
return 0;
( t* d7 q6 q& A5 i) }}0 q5 G/ h D; a+ z9 W9 C
void tVarArg(int num,...)7 n2 g0 k- h: Q& [. L, n/ v
{
) u3 o' R) o! `$ F! ~7 U4 L; iva_list argp; /*定义一个指向可变参数的变量*/5 X! g) a6 ]% N9 p A' m" b
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/# M. }6 s9 p% P. i8 R
while(--num>=0)9 j$ V2 h% _0 k) s$ {
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,+ } I& y' }4 Z/ m/ j O3 u
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
5 k( j# t; F5 X" H. A( ^va_end(argp); /*结束可变参数获取*/
/ e9 A% e3 I/ f/ q; }$ C0 T3 k, lreturn ;7 Y- n$ ?2 z4 Q! t$ y
}: M9 z/ Y5 v( p" f9 c
5 I4 w) @) Z7 {0 O. F4)可变参数的使用需要注意的问题
+ F v8 Z2 N8 a: s4 z2 t
) A) ^4 V0 Q/ }( I r- o1.每个函数的可变参数至多有一个.4 H/ C+ T% q) ]: M$ l1 P
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
" L' F/ [3 e3 F! t! z7 L$ M3.可变参数的个数不确定,完全由程序约定.
3 ]% `3 E! P( V4 _2 v4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换./ r ~2 v/ G) B6 m1 z
而printf()中不是实现了识别参数吗?那是因为函数 e6 s& Q! J: g
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg % c6 g6 M y1 O3 R! o1 ~) G* r
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 % W% @9 A$ B; F% h
过在自己的程序里作判断来实现的. w7 t+ F M% V+ p L+ v5 v
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|