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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
( P0 ~% f( `7 z4 F6 Z6 H! |, v) F: U它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.9 ]( \. p4 s* X1 G
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不: [2 F, c8 [. A
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有/ y0 _7 C" T, N* b* f" _) a6 Y6 R
实际的名称与之相对应.3 _4 t) P$ y! Z. t
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间. ? _$ n4 R( X/ M1 y+ n
然而,更多地自由,同样也加大操作上的难度.
# f1 A3 W4 X) }以下就对可变参数的几个方面作一定的介绍.
# \; P. ^. d3 a3 ~0 c( U8 n" Y% R* ~$ Z7 ~. T( k4 h! q
1)可变参数的存储形式.
0 l0 d, a. ]$ b6 G/ J. A8 f3 R& i2 } g% I
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,0 N& \! [* s4 h& _
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.2 y. c! _. m$ t' O! u+ V
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
2 g; r s# U/ ~* Z$ }/ H* n; D这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.( ?# r: j/ ?) u) n1 w9 q
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
& p. t& j* g$ p. O! D栈区:
8 J9 H$ o6 l# w6 G
' l6 Q6 B% o8 s- ~/ G|栈顶 低地址
# w1 A+ n* P) @$ h8 M- c$ ]0 V* F9 @" t
|第一个固定参数var1
$ s; ~+ _: N! N) R|可变参数前的第一个固定参数var2
J8 F# ]! N+ H0 _7 Q2 c|可变参数的第一个参数
% Q& _4 e! z9 @) `+ r" G|...
! K( Y. n' Q2 V4 D# f|可变参数的最后一个参数
d# T' A A3 N9 ^( o|函数的倒数第二个固定参数var3
8 o! R7 \5 H1 v9 Z* W9 |1 p4 w|函数的最后一个固定参数var4
. B7 V% P) B/ K, Y# s9 n|...
h# H: ]. T/ {# W1 }|函数的返回地址8 ?& q5 p3 K# `2 M* S5 w
|...
( p8 \/ u* J% v- k|栈底 高地址
. ~# y. T# D5 ~% w. o! L3 D
1 q" }) ^% |9 X1 z2)使用可变参数所用到头文件和相关宏说明' o/ q5 Y. b8 g! I$ P( G
1 ^; p5 D5 F2 i' b. `在此,以TC2.0编译器为参考对象来说明./ V+ U9 K" C, D: @2 N1 t- j' b
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
& J' ]! b, }- z" x此文件为:
2 |, O0 a7 f$ }; Z# Z/* stdarg.h
6 ^0 ^3 x* a; E3 k7 A+ D6 j1 y1 b3 y! ^1 D' o9 n5 a @; U& E( H7 M
Definitions for ACCESSing parameters in functions that accept4 X r7 x" Y, W8 r4 Y! }
a variable number of arguments.
" L, z1 r4 [2 k" M5 a7 K- ?
\% h: |4 w m3 _; UCopyright (c) Borland International 1987,1988, d# q/ e: j/ ?$ K5 Q! g
All Rights Reserved.$ ^5 d+ e: b% d; l! i, G! k- L
*/1 O) D& T3 I- ~% M2 u
#if __STDC__7 N1 V: m) N6 e
#define _Cdecl
& o" `& R6 z9 Y4 c) t& `7 H#else! m6 p! {% l/ E
#define _Cdecl cdecl
, M9 w+ p: Q' x9 f0 n#endif
; A) _$ |, G; k) w* A4 Y. X6 P/ j1 M" j
#if !defined(__STDARG)
; V+ l9 A# O# w. `! t#define __STDARG
: i2 m0 \/ \' G3 @! i! [) f' h" D( R3 I, J5 f' C8 \
typedef void *va_list;, p u( u4 }9 g! E5 N( K$ z0 R( ~9 t
+ A- ^/ X1 i3 O9 u' L" ^! i
#define va_start(ap, parmN) (ap = ...)! N% g- @6 }% _( L' u
#define va_arg(ap, type) (*((type *)(ap))++)
; q' r4 y4 M& w6 f) k#define va_end(ap)3 E) y+ O) D! z% v
#define _va_ptr (...)( y7 q: R, K, p+ Y/ ?) l; O4 D
#endif
# c2 t0 X: X, H1 V: Z0 y3 R1 E# Q; R
1 i3 s) S3 n4 t以上为"STDARG.H"的内容." Q! q: P/ A& G9 ~
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
# J% r! P$ \! x& u/ jva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
7 ^$ J9 Z0 S; R, AparmN为可变参数的前面一个固定参数.
% E' U: d) d/ D' P# A k; Dva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
" X! u0 t, |/ A/ jva_end(ap) 结束可变参数获取.- x5 R. ^; v1 \& R
( l- ?7 [2 T* d( w( y' M' K, X3)可变参数的使用实例
- L; r& C0 T0 V/ Q6 X `& A' Z7 b6 }+ b
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.6 Y- H0 L! V$ c0 ^' i7 P
8 X1 r6 K: v: J
#include<stdio.h>3 W3 e% V- m* Q. E1 P5 A* x2 v$ T: f) Y
#include<conio.h>
3 {! r' T4 X6 f9 k2 ?: W5 Y) l$ s#include<stdarg.h>: S, r* D+ r9 r* Q ~' }
void tVarArg(int num,...);/*num为可变参数的个数*/
. z" G5 O) T# k' n% x, P6 sint main(void)% q3 D& {9 r7 }$ \
{
; h6 l4 B3 a$ R0 g5 M& a% Wclrscr();1 v7 f( j/ d8 H8 [2 Y
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
( R9 {/ i4 H: l$ Q8 [3 m0 _) \tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");; u) v7 b5 C4 K
getch();
3 v" C( K/ ^# c. t7 p) freturn 0;6 X3 e& W% ~4 R3 }
}1 {$ X% u' D+ m; N2 @- _ t
void tVarArg(int num,...)
8 V2 T2 Z5 |/ r# o# k+ R, O% ]{! x4 P, S2 _4 k. T( u% t
va_list argp; /*定义一个指向可变参数的变量*/; M" n6 k7 h: m% `# l P6 H( g
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
0 Q4 C7 e( r$ m" t( P3 qwhile(--num>=0)
$ Z6 l3 X/ r4 l' [ printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,, Q. \4 t0 t! D' o* n
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
1 X6 a! H0 K( n) Rva_end(argp); /*结束可变参数获取*/) K" u. l, G+ ]2 Y% A; o; T3 y1 Q0 g
return ;/ U( @+ x" H: {6 M% x0 @) ?
}
5 E$ c4 B6 K; w3 b) @/ D
; r- A5 T" p$ O' Q- T8 d4)可变参数的使用需要注意的问题9 A5 G5 w; l! z! t0 d, F
2 c" K6 c( V/ A! r c
1.每个函数的可变参数至多有一个.
! Q% D- F- t' z' R5 Q2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
8 B; Q) B6 ~* v0 `9 O" w3.可变参数的个数不确定,完全由程序约定.
/ p; W% N# o7 N5 d. k& {4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.6 D' G1 K5 `% V7 p% T
而printf()中不是实现了识别参数吗?那是因为函数 ) ]* F6 D0 l7 b$ x/ Y, K- w6 k$ Y+ u
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg ! [8 C% I/ B/ _
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 4 Z3 ^- n8 V$ r( E; [
过在自己的程序里作判断来实现的.
: |; G1 |; Q, e3 N5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|