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

函数的可变参数详谈

可变参数的英文表示为:variable argument.- b* U9 x3 _8 M, b2 ^' \; o
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
2 p" h; t" s3 K8 M可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不  z, J$ P& G+ p# C' E; m0 x) [
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
5 c* L0 A. W% A8 H实际的名称与之相对应.
( X7 o' _" [! f7 l" b. I/ C由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
9 T* J0 ]0 d- S; g  _然而,更多地自由,同样也加大操作上的难度.
, O$ P8 p# m, F4 \4 s$ f以下就对可变参数的几个方面作一定的介绍.
/ c9 r+ V4 a0 P  ~* K
; N* ^1 n9 \2 `0 O% \& T: ~9 n1)可变参数的存储形式.
* @# f  |4 g2 ^+ u8 H/ P0 E, y. \( x: m
8 P5 P' g2 o# H; ]: I1 t大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
4 [+ o: w, v+ L) }存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.% l& o9 `5 T! B+ _$ W! R
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
9 A; n0 T2 d6 Z( b. C3 b6 k这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.# G/ N0 S4 G0 V$ x% g0 a% ]
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):! a) x7 Q9 c, d* B
栈区:) g3 Y& U" U' D6 b8 I2 f0 L

; R8 e# q5 b# L% }0 Z|栈顶             低地址
) ^# `. ]0 C- h4 O" f1 N# F- t4 o: G+ ~) \
|第一个固定参数var1
3 T: n$ E  q6 U|可变参数前的第一个固定参数var2
6 I2 x$ l- l: s5 `* x' a|可变参数的第一个参数
. U' p5 b7 A2 T7 g' c* t: h8 E3 g|...  Z% C* y$ K0 q5 E; q5 E% x! v
|可变参数的最后一个参数  U- z# U0 }/ J0 r- H# D
|函数的倒数第二个固定参数var3" x, H' ^: m) _! ~9 u( X; ^
|函数的最后一个固定参数var4
* T1 R$ W  e7 Q0 N6 l|...
0 q% l" r* C- m7 y7 R|函数的返回地址
7 |& W" M4 z) w! i( ?% D|...
  ~. [6 L( h' i|栈底    高地址
# T# e+ t. P$ d6 b5 e
! k# w% `. t% x+ t2)使用可变参数所用到头文件和相关宏说明2 F# A  Z- B! n; f( m0 p
1 h) Y  ?1 [7 |" E$ R7 n
在此,以TC2.0编译器为参考对象来说明.
; T6 ?% |$ t2 L- R可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
! N6 v2 T- H- Y) ^6 R( w+ j此文件为:7 M) L! }" f! J3 i! w
/* stdarg.h
0 W/ k# Q* h" _8 z* _
7 o" N; |) D% j3 gDefinitions for ACCESSing parameters in functions that accept9 C2 a  Y7 ]. I0 @) w% h
a variable number of arguments.
. H# H5 F8 M1 r1 L1 @7 _3 U  `. u/ H1 \7 M$ f! s% l2 P
Copyright (c) Borland International 1987,1988$ t! P6 n- F# l! v3 v& @
All Rights Reserved.
' H3 p+ O# \8 u/ L( d*/5 U# s2 r9 t: E+ n
#if __STDC__6 E; H* m9 ]0 X9 W9 d# S. h2 {( C
#define _Cdecl
5 R4 |1 m  f+ f) L& I; Z* A#else; ~" j2 t; y# v: I. o
#define _Cdecl cdecl
( P! m( R4 H+ W; E( Q4 f#endif
+ x; c# }! D! S# z+ Q- N2 H
7 F; O7 K3 J+ r; _! L" g0 S#if !defined(__STDARG)
2 m# u0 u  c8 Z# `#define __STDARG' m$ o( O- a7 o8 g
/ p# `% D1 h% u
typedef void *va_list;7 e$ b% J& _3 `& j
+ {0 L0 [5 ~! @/ X" ~( C& h6 x6 X  r) o
#define va_start(ap, parmN) (ap = ...)1 U5 O1 H6 O$ ^5 V
#define va_arg(ap, type) (*((type *)(ap))++)  u$ l, k' \. m
#define va_end(ap): b, h& {6 \" D3 \: K9 j2 O! G5 z
#define _va_ptr   (...)
) _8 }* i' U1 ]# H7 ~$ }#endif
! L! j1 p6 z( Y6 b$ k5 S2 j: N* e9 H
以上为"STDARG.H"的内容.
& r1 T+ G) T1 q9 W) c! Q该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
) w) o. s) b; n9 H. u9 N( {) x' @va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,& d: [! h- b" L: ^5 s0 Z& B7 ]1 e- W8 r
parmN为可变参数的前面一个固定参数., a8 D7 c4 Z  l0 }2 M
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
# G1 P0 ~0 l# H2 Yva_end(ap) 结束可变参数获取.5 i2 B8 I# p( Q' X# `- i: w# a2 q
6 `" ~: k3 q1 E" ]% {' T
3)可变参数的使用实例2 R" v1 G+ q& J1 v! Y
9 @3 u  I* _+ p$ V/ _2 L7 R; ~) h
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.2 v3 ]/ @/ t& m! O+ |, I; `

( L1 h' J. Y3 r% `# R7 l#include<stdio.h>
; z8 S' a5 l, v#include<conio.h>
& b, C! }% }/ z& h8 e2 y& J#include<stdarg.h>
3 o$ S# w( F1 rvoid tVarArg(int num,...);/*num为可变参数的个数*/3 X$ ^! H* H& H' F5 m
int main(void)9 w! k4 b/ M/ W0 H) z$ a+ N6 h
{$ n- n1 ]: i0 ?8 N3 q
clrscr();
4 L' W; Q" K: `' a5 m0 [; _tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");" [' V( [: k* f9 ]
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");1 }" x5 S% M1 I# S- _$ A1 k
getch();
  o: d, Z% E3 l" T0 r+ Ireturn 0;8 {2 \3 }+ G, v' _( K: l
}
$ X; M& J4 I+ T7 A$ @void tVarArg(int num,...)) p' y8 l; @! p- A6 ^+ ]
{4 t. r8 F% h& j6 I0 V! q
va_list argp;  /*定义一个指向可变参数的变量*/: t! |2 r3 I. Z
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/- }) F$ L; b" ?% l8 P5 d
while(--num>=0)
/ L; P4 D5 P4 F  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
! [+ Z: S6 X' [! ~; w; h    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
7 a' \8 |' d/ b* _; Nva_end(argp);  /*结束可变参数获取*/
. [3 K5 d: L" breturn ;4 }( y. F- s0 k
}
* q: ~: M" b6 f' y& p( k' A7 u$ s8 g  v+ N3 I, D4 b
4)可变参数的使用需要注意的问题
0 q6 F% o; ]7 U0 B5 C) M3 A; G2 E, c
1.每个函数的可变参数至多有一个.
* }1 d  a# |4 m" S, @$ f: p7 T2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
1 L8 k7 E4 l) Z/ t* s% W7 M3.可变参数的个数不确定,完全由程序约定.
% a% Z+ ~. W( a4 N  i# ]4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.0 y4 l/ H$ F+ A# o4 |* r6 a
而printf()中不是实现了识别参数吗?那是因为函数
, R+ ?; X, r( j" b! D' W5 @printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
) B. O  [: N$ M# e的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
5 q, T  l' f, C3 R& ^过在自己的程序里作判断来实现的.
6 ~! X( C. b8 T2 h" c& ?4 U( M4 s0 p5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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