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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
5 {$ p$ @9 U) h. x* ]. Q它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.* D& M5 q+ }( V) M1 }
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不8 A8 i6 l1 c& y& X+ R1 h( z7 G) e9 U
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有; z4 x0 \0 h$ E8 ^8 L3 ~- ^
实际的名称与之相对应.
; _$ b/ ?1 B/ \: R" g由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.# a& v, U% @% T5 f2 f
然而,更多地自由,同样也加大操作上的难度.
' a' `" y! A4 @; k以下就对可变参数的几个方面作一定的介绍.
" ?7 D( c0 a6 S _. p; J N
; a, G! e, u; W1)可变参数的存储形式.& z) H$ [: L5 z5 W
6 e. Y% e# ]/ j
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
# C2 _- a- E3 \( O; t9 H存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
* W+ l' }: u) p7 n. d- |. T在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
, F3 P( C5 U3 V( T6 f. R这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.9 |5 W1 m: y& J! W5 m
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
; I+ X6 q& D# S栈区:" P, K' B1 H" R @8 r0 U3 F- G
: A0 U4 n4 T2 ?
|栈顶 低地址
2 Z) p7 M' z8 ]! a3 L' A: j- A. Y+ V3 c7 Y+ n' E H+ u
|第一个固定参数var1& N: z/ T$ t# e c) C- P
|可变参数前的第一个固定参数var2
2 ?- F) @7 l8 c( m+ N8 B1 G|可变参数的第一个参数
6 \3 s! q: [7 p|...
- o" E$ w" ]* T$ D|可变参数的最后一个参数
9 y- m( n0 l% \/ N6 U|函数的倒数第二个固定参数var3
# ~- `4 m* R* |4 L+ Y( z|函数的最后一个固定参数var4
h" m# x0 d$ W" S. e& L" s: H* u|...
@# V2 P) T3 O5 r4 v( s|函数的返回地址
: V; U! ^" K g0 ||...
* e5 h1 s$ s5 F! W+ @2 b|栈底 高地址" {3 ^ L" E1 ?
1 {6 D: F: I3 c, x. x0 K2)使用可变参数所用到头文件和相关宏说明: { B# P. ?$ w* P
3 D9 G$ O6 F1 d: V$ G6 u在此,以TC2.0编译器为参考对象来说明.
: O5 h h- ^) z4 s2 w可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
' T. w, N H( z; p此文件为:" L% P3 q" \! R
/* stdarg.h- q( H+ _0 t- g/ k( y
9 H, a' @5 Y0 \/ P0 s. A/ tDefinitions for ACCESSing parameters in functions that accept
* J' ?- @6 k' ]4 [ b' |a variable number of arguments.
0 p+ E/ f2 N# s; H9 a, }
, [& j/ z* O3 }( k& ^Copyright (c) Borland International 1987,1988
, c5 z" @/ v9 }9 Q/ hAll Rights Reserved.6 [+ R" y9 w( N* }1 U
*/) ?4 Q- c' B( H) ?7 [6 G4 a# ~) z* m
#if __STDC__- G$ m6 P# z/ |# C
#define _Cdecl
& u" O1 m9 H! o5 K9 F1 r# U#else
, u _- S W5 M2 a" Z7 k#define _Cdecl cdecl' \ O4 X5 H7 ]7 }# Q
#endif
4 j/ J R# n4 x" A k% ]# p+ T. Z6 S- z- V
#if !defined(__STDARG)
) {% N2 W4 Q! t* r( o#define __STDARG
" @" I& r* E- l. H0 K
3 _/ }" L8 h2 c/ H/ [4 c, ^! dtypedef void *va_list;
: R& x3 ^8 x- v) q$ F7 d3 t: p, `6 W+ D; I6 i
#define va_start(ap, parmN) (ap = ...)" n, t+ @- J4 X/ W" h1 {
#define va_arg(ap, type) (*((type *)(ap))++)5 ?5 P% l5 f7 t4 b5 d1 o
#define va_end(ap)
/ @/ @1 C% U/ |% N- H, @" d) l8 [) p#define _va_ptr (...)
( M: |/ n8 f8 G8 M. Z; x#endif6 X1 ~/ h h3 O$ ^& L
, Z( q2 t" w6 B% g, A以上为"STDARG.H"的内容.
0 H- h6 E+ _) z/ A' i% k, a' u该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;0 h4 |+ l: c# {
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
' G+ v _+ Y( ?: _0 A* p uparmN为可变参数的前面一个固定参数.
0 _' m0 M( S7 [* vva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
$ S! {9 w t& ~5 bva_end(ap) 结束可变参数获取.7 D. R- ~' |5 r9 E& D) [% x
/ X4 J! ]3 Y: `- s; k' v3)可变参数的使用实例
3 Q0 S" C4 b, T6 J& C
; {1 d/ `9 |/ T6 I" A- ~4 B! x实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.& p8 E* t4 F6 m
2 W! k" |$ G0 y$ b. g#include<stdio.h>& u' u9 a8 X9 ?' a
#include<conio.h>" p0 n( T) W3 Z- m0 X% i
#include<stdarg.h>0 b* o [, K4 f6 S; `" l
void tVarArg(int num,...);/*num为可变参数的个数*/
+ e r. _# m- Uint main(void)" F! ^0 o+ J9 w
{5 M, @3 G3 \# y9 l: x' J& M0 h$ m2 i
clrscr();
3 T' F+ G! W8 t9 q6 S9 ytVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");- l8 m+ W$ ?* `+ p1 u6 ?0 ]7 ], W
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
1 N$ A) z: j) n2 ygetch();) H0 g# u% q$ k- p' l) a5 U
return 0;8 y6 ^* b R' V
}' c! c1 U! E j* b+ \6 j
void tVarArg(int num,...)4 K3 j# l% v2 r9 c$ R! j
{
5 W6 u) V# p& r2 N, vva_list argp; /*定义一个指向可变参数的变量*/
/ T. @* R; p" @6 \$ @8 k4 i/ _, xva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
" E4 v" H$ E8 I, }0 o* ~6 Q! @9 dwhile(--num>=0)/ M: X7 d( }, q& k
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
! o* e, u: N. ^% b, P 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/$ c0 ~$ q; U5 P3 e" M: ^/ Y* f
va_end(argp); /*结束可变参数获取*/" u% o3 f% x$ ^2 x: ]. `5 }* C
return ;: v3 e- M% w- s
}
k R: t- M+ c4 c \/ }
5 O" G# I2 O3 \6 C4)可变参数的使用需要注意的问题
. n+ K( q: Q% k7 g$ w& \4 W; \( t6 }' u2 D. Q1 q$ N$ c1 k( Y8 l" X
1.每个函数的可变参数至多有一个.! | z8 Z8 j7 D A1 ?
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
% Y. d3 F, d! `; @, c3.可变参数的个数不确定,完全由程序约定.
I" I! Q5 g9 _( W# n9 H4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.3 w( b/ \8 }) P1 H$ m9 r
而printf()中不是实现了识别参数吗?那是因为函数
8 L8 m3 ^5 ^, J6 _7 Nprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg , [" c' x# s: |0 o5 b4 F" y
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
( h! T P. Y; X: F6 m过在自己的程序里作判断来实现的.
- \" n! _8 k$ h+ A. e5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|