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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
$ g X- Y' w3 q9 h$ ^1 W它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.$ D* [6 }7 ]7 C* j3 ^) i& @
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
+ M" l6 _" @/ B6 o定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
# I+ i( w( p }+ Y实际的名称与之相对应.: l, h5 L1 S! ]+ g& Y/ l
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.& h; P8 l; o5 i( ~8 h
然而,更多地自由,同样也加大操作上的难度.! Y) O4 h- a8 y z; E( ~
以下就对可变参数的几个方面作一定的介绍.6 s/ y8 F7 U2 U( ?( T; r5 h0 {0 o1 E
! F p: J- U$ ?( U8 w; ?
1)可变参数的存储形式.
/ L4 h: S' I6 {3 h
& W/ x* T& q" j7 q9 h0 L4 u% R大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,; T8 |) H+ b$ j. c) f5 r
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
1 U4 ?9 G# ^/ S, w+ K' F: H9 I6 {在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
. }- V- |+ i" [9 [+ M; o8 P3 b i( W这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
/ V( i1 h( w8 J因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):% D& _, k( G8 J# C% u1 \
栈区:7 b8 Q6 ?+ L, p/ m2 m2 [& d
# ~6 D# w( H7 e9 [( A
|栈顶 低地址( N/ J1 o3 v$ x9 N, M
. k2 i/ {$ r. ?! [' y|第一个固定参数var1
9 c& G1 b& b( B3 [; g5 p. t. M|可变参数前的第一个固定参数var2
# z5 ^5 h. r( D. g( s|可变参数的第一个参数! j! z8 i9 M1 X: q4 R# S
|...
. ~; i' |! c& @1 r% K|可变参数的最后一个参数
8 J# ~; A% p+ \" J; }7 Q ?|函数的倒数第二个固定参数var32 _2 e) \, G+ z. {4 v
|函数的最后一个固定参数var41 L" {% k7 t+ G1 q6 t
|...3 S* {' \/ T; S
|函数的返回地址2 F& W' {& z. D+ I8 G# q
|...* L$ [* [0 J1 m9 v5 x- x
|栈底 高地址
7 f! ^8 q" q6 S' j& w6 _ p/ B. j* z. z- w
2)使用可变参数所用到头文件和相关宏说明: ?' W, ?' ]; y1 C0 a+ T! o/ s# b% Y
; h8 x2 N. t6 P9 c在此,以TC2.0编译器为参考对象来说明.2 C3 x: T; H# T0 E4 O
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.: T6 C2 d1 `5 s7 N- _
此文件为:
7 z/ O0 }' @" k" ^/* stdarg.h' X- g( ^# a$ W4 P* B/ d; B a c
^, a- q3 S, C
Definitions for ACCESSing parameters in functions that accept
1 A. [# D; f6 z, D# u- ?a variable number of arguments.+ i; t9 T. ?0 B' q/ H
% d* ?) }, S, ]# cCopyright (c) Borland International 1987,19883 n& I; D0 J" v2 O
All Rights Reserved.
- H5 c2 g0 g# s$ D$ z*/
/ _ l7 x0 B: Q7 c9 I% s3 W" J+ @& C* S#if __STDC__( U; c, a' B/ H* e q; N$ a
#define _Cdecl
" s% x" v3 b, s7 D: Q7 r: F# a- L#else
$ p. p" P5 M) o& O* q' r, S; F#define _Cdecl cdecl
1 Y$ z* z( m0 `& Q% n- U1 a#endif
" g* ?5 l( x+ ~- M. V9 \; E
% S, V9 j6 `( H4 B4 S#if !defined(__STDARG)
' L5 J7 S; h$ N V+ i#define __STDARG( {3 d# U `! @6 ?
5 F) ~7 q$ {+ m" z8 B' Itypedef void *va_list;/ `; D* f+ c, b6 m
4 o1 d- j- f, T0 O! i( |#define va_start(ap, parmN) (ap = ...)& i* o0 G; x5 g V1 O8 D
#define va_arg(ap, type) (*((type *)(ap))++)
. N# w; c! H0 a/ k1 B#define va_end(ap)1 p( I/ E, e1 C: a+ A, {( I7 \
#define _va_ptr (...)1 U* ~; L! {, |+ i1 ^( ~1 u- p9 z
#endif
$ c8 {5 @: {2 \( i5 O9 r. b6 U2 D
以上为"STDARG.H"的内容.9 _+ L! d( ?7 R, u
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
! g: b2 J+ a |" Bva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,5 \1 h/ K; n) @- t( _, x1 u
parmN为可变参数的前面一个固定参数.
) i) b- u2 _6 ?6 ]va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
/ `2 J, T* p s8 C! w8 z/ Ava_end(ap) 结束可变参数获取.
4 a+ F f- o1 U7 v7 D
; K2 ?7 I6 i( U3)可变参数的使用实例/ C2 z7 l% h* ~
$ P$ Z3 A) d$ r, a9 K
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
- _2 B) U& I3 K7 q3 [: H
' O6 O0 G. H: Q#include<stdio.h>2 o# s! `. t% p& I& ^" o/ b1 n
#include<conio.h>1 Y' }( _; C% j0 p% m. ^" K
#include<stdarg.h>
" }6 @0 i* s" \$ @void tVarArg(int num,...);/*num为可变参数的个数*/
: L5 o2 y4 r) Zint main(void)+ Q, r$ A. V) P& a/ W: ^
{
! [6 C1 M( E( ^' sclrscr();
1 ]! V6 V( t' O StVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
; s, b! |/ Z" c7 \! HtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
3 h' ~& ?2 {, a. B: p# Y- p* mgetch();7 X1 p8 J ~4 A1 f3 ?( e- w: Z
return 0;
- e6 h t" ~( E. r}
' ]0 E4 e) Z$ F' }void tVarArg(int num,...)
5 `' K) {+ B; n9 a4 L{5 e2 z: p/ y, k/ x: k$ d, D; @
va_list argp; /*定义一个指向可变参数的变量*/
; _9 s6 t$ G: I/ ~3 A9 q: Sva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
0 C6 _: r, D1 d8 c, ?, \while(--num>=0)
' B7 N$ L; Y* d# C' F( j' ^ printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,0 V4 j) e0 V# J( }: M
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/) ]* D7 T. c; T+ \
va_end(argp); /*结束可变参数获取*/0 W* J4 P: _# i( x9 R
return ;
l4 A! S+ [2 z3 J3 @}- t; d j& z5 B/ V
) u* Z/ w5 s! C" A; M4)可变参数的使用需要注意的问题9 X8 T6 `9 {3 o' M
* a# V- B; ?1 I" G K" [' p1.每个函数的可变参数至多有一个.. [! _) J( r! A
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
5 u+ W) x' ? o4 n" n3.可变参数的个数不确定,完全由程序约定.
/ C( S5 x2 G9 x+ p. C4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
* w& w! o- Z4 _而printf()中不是实现了识别参数吗?那是因为函数
" B4 [; f5 z8 k' q) s$ _printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg * K4 y8 _1 D- J
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 # |$ R* V- P0 _% ]0 l4 a
过在自己的程序里作判断来实现的. # p1 V. P! F0 ~7 Y. t" C: R
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|