获得本站免费赞助空间请点这里
返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.
+ j& g6 [. u1 \- K; b9 C! v; ]它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔./ }8 b2 Z: g; F$ F: L
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不% e$ [. K6 p. f( c
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
9 }8 W% O# Z6 f4 E) {# c实际的名称与之相对应.
9 g" g+ Z3 @; u9 e0 M" c) b0 K0 w# |由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
; S# p: N  b9 _然而,更多地自由,同样也加大操作上的难度.
9 U% i5 \* n0 b5 N( v0 C/ P1 r以下就对可变参数的几个方面作一定的介绍.0 _0 J5 Z* J! U5 `* N" F% l

! W6 E8 L+ y- G; T' |1)可变参数的存储形式.
* n& i9 n0 S6 R  p5 y# e& s0 M$ ], X# L& P# Q
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
# t: Y) P: O. S  K0 l存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.. s8 j; D3 P) ]" q4 Y4 V2 `3 [
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
3 \3 d" k/ ?  y8 ]这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
& q' M& D) V8 S0 y& A因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
# \& Q; q  \7 V栈区:
0 v2 T8 ^+ N- X( |! W
0 z3 e  r5 J  ?3 y1 o  M* D|栈顶             低地址3 O; G) x% {/ w# B$ Y0 e) Y0 `( Z
3 M$ W, G& P! l. D9 C' {
|第一个固定参数var1
7 a  F6 f; n& N# y|可变参数前的第一个固定参数var24 L% u% o. N9 r
|可变参数的第一个参数- c% b' z6 W+ C4 U- z
|...
2 q0 L7 e4 w& G|可变参数的最后一个参数
- }3 P; W: \9 D# B6 p' D|函数的倒数第二个固定参数var3' s  p5 W  c. `* Z0 P; _
|函数的最后一个固定参数var4
# w+ E& o; d6 I" n|...
0 ~/ ?+ y  |6 v9 ^1 C& H1 O|函数的返回地址% q. A+ y* W  e0 i1 N
|...
9 g' u+ |9 T2 [, ~  t7 @$ m" q|栈底    高地址8 y# o3 m) A; o9 B
- l' ]# ?+ ]. i# H+ _: C' C
2)使用可变参数所用到头文件和相关宏说明% T. l' ^( D3 |3 a/ v
4 b) K2 A# H+ B  T
在此,以TC2.0编译器为参考对象来说明.9 @8 ^# @6 |# m1 _6 d
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.! K3 J2 b3 t% [) ]# C$ H
此文件为:! `" `5 S, W" T* {: g
/* stdarg.h
4 Q3 H( ^4 Z: a" e" C% m2 S% ]. E3 y. z& m. N1 S: R  l; y- N7 w: N
Definitions for ACCESSing parameters in functions that accept5 I5 \/ q& \: s
a variable number of arguments.
* p9 V+ m+ K0 s/ U+ `$ V
9 P( F" u& E# z5 l# ]0 I& wCopyright (c) Borland International 1987,1988+ z, V3 D$ m# R# M7 Y
All Rights Reserved.* \' j! P9 c* v" k: t
*/
7 Y: k% [. e3 o& \2 B& F) ?#if __STDC__
/ k, p& |- ]# J5 M+ x7 a: U; Z#define _Cdecl+ c& ~% Q% ], F* Z
#else' Q6 `" w6 t7 w0 y+ c  S* d, J7 v  m0 d
#define _Cdecl cdecl; H: A8 h' m9 y, Z6 j* `
#endif
+ W& ^2 m3 _( T! |
  u- T3 {9 u0 f( M8 F, g#if !defined(__STDARG)
& x( K% |1 F7 G#define __STDARG
/ z" z  y& M% G, C5 Y! a( \6 a$ b( a0 K$ y. K. ^) y3 T% {. d7 g
typedef void *va_list;
2 M, @7 g+ v3 T$ D# f. h% W8 u& T* p+ P8 r
#define va_start(ap, parmN) (ap = ...)
- s# f) r* s" @$ {. v% j8 v#define va_arg(ap, type) (*((type *)(ap))++)
, Q5 Z, N5 h! n4 |4 A#define va_end(ap)
8 C  P* I8 s, b% O! U/ L! F6 u#define _va_ptr   (...)
, y; P2 u: C" E, @, _#endif
7 J* |) Z# A8 H$ I. U; W, x. p+ |3 W5 [7 G+ ]
以上为"STDARG.H"的内容.+ N6 }1 U# o2 v( h
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
& @. m( \& y% q! h6 i; b9 b' @) j$ A0 Yva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,1 s( J% u% t0 x4 e
parmN为可变参数的前面一个固定参数.
) U) {2 J! v; y$ ~va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型./ g* P% l9 N8 d; i7 h0 ~1 p& m3 ?8 p
va_end(ap) 结束可变参数获取., d1 g# h% `* _) x- ~# w

4 J. b* Z, m% e$ w2 T8 K3)可变参数的使用实例
! P: R% Q+ D# l8 I$ _) D8 z0 r
7 D% c- I- I( s) W实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
- s/ Q9 F9 N7 ], K& c
( h( p0 i* X: u5 q6 v6 @, p/ r, k; P#include<stdio.h>6 g" k4 {6 U4 c$ z  J, I& s
#include<conio.h>7 i$ @8 q3 \# K# y+ f! h
#include<stdarg.h>
- n- N; y2 ~3 Q0 I, i; Avoid tVarArg(int num,...);/*num为可变参数的个数*/" Z3 D  T6 i3 L; X7 Q
int main(void)
5 Y' |/ }* b8 q* m{
- b+ Q2 ]- f9 x1 fclrscr();
& f+ J* B; {, \( y3 RtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");& b$ \  z, i; T. j2 n( ~% n' U
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");( M% w# `) v  a; h; V& {& \/ Y
getch();
* y2 E( R- r) k! ~return 0;& c! d" G7 _2 Y2 I' u& P
}, ]! J( y/ m7 N) f8 o8 G
void tVarArg(int num,...)
/ C$ F& y8 i3 g  \* g4 f- O9 z) c{
" ~' X1 n! p- [va_list argp;  /*定义一个指向可变参数的变量*/5 h& L0 D; L: B* a4 E6 `
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/; S7 `3 g5 q' w+ z6 i$ r3 q# U
while(--num>=0)  ~9 y- q" U6 W# @* z1 {
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
( B2 v3 A0 P& u1 j' ]    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/" V7 \' r2 o6 F" q3 u
va_end(argp);  /*结束可变参数获取*/8 h; \9 e( k0 @( v' d3 s
return ;
  ^2 n9 P+ `0 e# \: m5 B' H) }  w}
# c/ _* l. F7 |* U0 q
% ^, E# ~1 U6 H/ Q4)可变参数的使用需要注意的问题. Y1 T3 K2 K$ V# c" n' I9 b" B
; P! Q& g( x4 O5 T8 y3 k5 d
1.每个函数的可变参数至多有一个.- X( @* @! {3 A; f  S4 |: [
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数." g! J- f9 h9 g, k
3.可变参数的个数不确定,完全由程序约定.
. i+ Z, \- z9 |* ^4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.' B1 l6 l4 ^. }* b% h
而printf()中不是实现了识别参数吗?那是因为函数 ) N" z5 ]7 t' R7 l  Z- L4 J4 K
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
( e/ f  T5 H- H8 R, }0 _6 C的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 4 T  s0 T# {% m, \& h# `# d
过在自己的程序里作判断来实现的. 4 w2 W( w# S" F. a$ }
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

返回列表
【捌玖网络】已经运行: