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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.6 N7 [! q' G1 O- i
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.2 a& g8 g5 V' T# _0 u& r9 d* r
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不- }: B$ c. V& X, s# q
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
$ X$ {' d3 q: l# G实际的名称与之相对应.
7 e* e- L' W0 W! T( ~4 ~由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.: i" @: `# k+ r8 A1 k
然而,更多地自由,同样也加大操作上的难度.
" [* r" I0 C" y3 r" }+ G W9 D以下就对可变参数的几个方面作一定的介绍.
; a& A" h4 C8 }! s
8 D, X5 q' ?* b1)可变参数的存储形式.
1 E4 W V: U! @) Y* B% N# {5 s7 F7 x4 N4 e6 H# t; ^
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,# v1 E2 F% G4 n# \: H" d6 s P1 D
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区." M. X5 `. D/ N6 g1 Y
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,' ^# Q$ v& d$ m4 \: C2 I- ~5 x
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
- ^7 S' T; k+ c$ B因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例): t2 E1 s5 U9 g8 x+ K
栈区:
0 o* a% O _# e3 M d: ^* \1 V. r P1 `, z7 S& J
|栈顶 低地址& i$ v& [4 }, F4 E. @ w
; |. [0 o! V( z|第一个固定参数var17 p2 @, h* S/ z, ^$ z6 d0 W
|可变参数前的第一个固定参数var25 h- g. s6 E; E
|可变参数的第一个参数
2 n8 {, }( u6 w+ O7 r8 g( t, Y V|..." Q% S" q0 \2 Y4 J
|可变参数的最后一个参数
3 {6 K! ?; ]' v/ _|函数的倒数第二个固定参数var3# Q* K2 T" h* X1 c3 i
|函数的最后一个固定参数var42 z2 Z# ]2 P, t$ p) L( ]+ Q
|...' i/ l. n/ }- a [
|函数的返回地址, ~% ^2 _4 P7 K* D( @
|...
* Y% c7 R \ g! H9 A U|栈底 高地址+ i4 z7 `0 n8 C' x H0 `
: V7 ~) ~( h, p2 R9 H; g2)使用可变参数所用到头文件和相关宏说明
u0 K" p/ ^0 P, o7 N0 ]) \& R9 _7 e& y
在此,以TC2.0编译器为参考对象来说明.3 R n$ D- B, V. g8 K# Z4 ]
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.( c" M( H( X. p3 _' P# s
此文件为:9 J$ K5 y; q' s* e
/* stdarg.h1 u. Z1 O A) U# k9 V
# Z" R& l" ]% w e1 w/ v
Definitions for ACCESSing parameters in functions that accept! ^; d: o1 T3 D' P) C% T" K
a variable number of arguments.2 t, _8 N2 m3 K+ P8 S
+ [2 Q/ u" O$ U( ^6 M
Copyright (c) Borland International 1987,1988( V- o M7 }9 ~$ z
All Rights Reserved." g$ G! T; b# f6 `4 [5 X7 h7 b
*/
) f, v1 v6 ^/ \. ~5 [- a% D% ]5 ~2 [#if __STDC__+ S9 M5 p# Y' H" J: K
#define _Cdecl
! L# G8 ^. q6 P: k. {. i/ R#else
& U+ \ G; o9 F, F: G5 Y' q0 r3 W#define _Cdecl cdecl
2 j0 M( P3 ^7 c+ i5 @5 a' f#endif1 W0 [- ]. {! p1 o
, g* F G. I, [9 }# D; p
#if !defined(__STDARG)/ s+ Z: O \, i& r4 p" Z
#define __STDARG8 a. v) D% l1 b# v
i+ f+ S. a) W3 G: S$ D: B0 Htypedef void *va_list;
! J* N% v# ^, |0 C
# S- @) Z& Y0 @- w+ {, m' h#define va_start(ap, parmN) (ap = ...) @8 @" S* H$ i/ q9 |
#define va_arg(ap, type) (*((type *)(ap))++)# P: U; s E% ~( D5 L; l
#define va_end(ap)
/ m* C+ W/ K5 K7 w6 J#define _va_ptr (...)$ A5 A+ W+ ]2 g# b+ u* e
#endif0 h6 }: x, Z z
+ \) Z9 N+ R$ U/ X! J/ _
以上为"STDARG.H"的内容.
+ C* A! P( u T: S% i0 d, u$ a% ?5 b该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
7 _+ I5 g# U# P6 a5 ?va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
$ f6 n3 g" ~: G) b: N3 L, {+ ]4 P1 vparmN为可变参数的前面一个固定参数.! R/ D/ H) O" [+ T" Z# _
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
. ?0 \/ [1 P% E' @( Cva_end(ap) 结束可变参数获取.$ _& }+ U" |4 y2 x: V: M X
5 ?) S/ Y* b# X7 O
3)可变参数的使用实例
- y$ X5 L: `7 }4 Z3 k+ T( W3 i# q( O+ E u
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.8 f& q3 B, X' T1 o: }1 W1 z
( J t' f0 \5 ^4 O#include<stdio.h>9 i2 U" [( |# B" Z
#include<conio.h>
! B6 F( b! Z) H$ S2 K#include<stdarg.h>
% s x) j% Q, Q: p% avoid tVarArg(int num,...);/*num为可变参数的个数*/
& R6 z# o/ I4 v L8 Q6 f2 Cint main(void)
$ c) Y# |- j: M4 i8 ^& U, P{
7 l* |$ \# Y7 \clrscr();
" @4 Y! Y* Z3 ^$ D0 stVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
9 R' X. V# L. P' q) A" l1 S9 `( EtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");0 t. W2 e7 @6 _. \4 q* \. X
getch();
1 q8 o) Z! A/ _( R6 @return 0;
/ a5 d+ \& B+ X}
5 H2 F0 Z7 \3 a hvoid tVarArg(int num,...)
& h2 X% t& @$ F. Z0 N{: x( x: f i, Z$ w( P$ u0 ]
va_list argp; /*定义一个指向可变参数的变量*/9 O/ r5 j- ~. F
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/' d% }% Z% O: ?5 `" z2 D% `! Z
while(--num>=0)
# c- M$ A( M: _% v8 y8 ` printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,/ Y/ c+ [" P6 C8 I& M3 i
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/9 g) Y9 q+ C; R; L2 m) u9 j
va_end(argp); /*结束可变参数获取*/# B# ?2 W5 p9 B0 k! p Z" z' e
return ;: E3 q+ o; }* d
}
* u, H+ x: U* ]) i- O/ Y5 W: E4 B$ O- q& R& }/ g
4)可变参数的使用需要注意的问题
: @$ t+ d" O8 t& r" Z* F+ j+ |# `- e9 w
1.每个函数的可变参数至多有一个.
- e3 w4 ~# B$ z! n# ~4 ^/ D2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
% z! {8 r; `! _% W1 w* C$ K3.可变参数的个数不确定,完全由程序约定.% i8 D, b( Y" S: z4 s
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
# M: T) F# K2 e& R而printf()中不是实现了识别参数吗?那是因为函数
j9 s m) t* K9 K. V, [( p( t- a+ u$ Oprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 6 I; h" x7 N" t, r
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 9 l z3 }, B7 I; E
过在自己的程序里作判断来实现的.
( h$ E0 r3 N" e& G5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|