返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.
. i8 G' V! z/ N" R它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔./ g* C; U$ G8 N8 Y# s
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不  i! \; K5 I4 O5 }5 d0 ^
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有+ j: C! }1 P: S2 u3 ?
实际的名称与之相对应.4 e" E9 b9 Q+ n0 `; @/ g' H' M. s# a
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.2 x+ {* ~# I+ I, g2 W, n0 Y
然而,更多地自由,同样也加大操作上的难度.
5 ]$ c- p3 x! E) v1 y$ [7 B; d3 m以下就对可变参数的几个方面作一定的介绍.
4 Z& N) U  t) I2 }6 Y6 }; g% o3 A7 w3 s
1)可变参数的存储形式.* v. ?* x  t, U1 y+ M

, u- U8 l7 f3 [大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
0 \9 M- `: @* p$ Z存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.4 X1 `$ S. d- F/ y9 v: J
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,8 p# J6 H' F1 f4 {+ B6 T
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.1 A! t4 Y' J; I# D9 f4 ?6 ]
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):( l8 `6 O8 G) I- C) V
栈区:
: O% D; c/ L1 H. R$ q7 \4 d* h3 A) E1 r( P  _0 s6 I* q! Y1 M
|栈顶             低地址+ i; i& ^$ @+ l$ p
6 b. x) J  R- }+ n6 `
|第一个固定参数var12 k  S% g$ D% t7 P  j8 @( X& L
|可变参数前的第一个固定参数var2
% U4 I# _- S4 U. e|可变参数的第一个参数
8 v, ?6 d( {4 i; F/ A|...6 x  N$ ^/ n" o$ e0 A/ P  N1 p
|可变参数的最后一个参数: r# `, I9 k4 M, _/ U3 `
|函数的倒数第二个固定参数var3
! ?3 w1 o. {: L  d* p, ~' U1 [: d9 O|函数的最后一个固定参数var4
& I( x- Y) C+ M; e: ||...
# ^; w6 t" V  K|函数的返回地址" K9 X5 p4 H+ G$ K2 Q% y
|...
0 {9 }& |) e: }" g# F$ M|栈底    高地址
: w" {% O! f7 w* o0 K5 P+ P! F; p: M6 U" T
2)使用可变参数所用到头文件和相关宏说明
( J, _" I6 `7 z- e1 k2 P
9 o  D- L' {9 ]+ y0 ^2 y' N在此,以TC2.0编译器为参考对象来说明.
. G2 E( ]5 O2 N, Y9 d( B可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.% k+ L/ k7 ], A1 [8 R: F9 A
此文件为:* l: X' R, b) S0 K# a
/* stdarg.h; H2 Z: N* S+ t8 n" F
. }# @1 w/ v, N: B! O' n/ g
Definitions for ACCESSing parameters in functions that accept
: Y9 }0 R9 v* E+ i- Aa variable number of arguments.* q- o2 k  [2 `4 Q, \$ p2 q
) [% R3 G9 X. g5 m. E
Copyright (c) Borland International 1987,1988: N- D  {, V0 u. ^
All Rights Reserved.$ J+ h1 K# ^( O4 y/ g3 P1 S
*/
& r, z1 t/ u' n% W9 L' a6 h+ \; M#if __STDC__5 x- D& E, u2 _! v1 F
#define _Cdecl
6 q  @0 B% [# l; G#else
- r; X0 [7 z7 D! N8 l! s) L#define _Cdecl cdecl/ J. \8 x/ J; L4 ]* D1 C
#endif
0 g1 q' u0 K2 J: ^4 s, ^8 ?6 t; N- u, N
/ ]4 ]7 {& b) B& J" t#if !defined(__STDARG)0 y4 G+ e; I7 I- B7 {7 a4 s
#define __STDARG
% @, R0 T; a- f6 x3 u6 O5 [" D3 U
typedef void *va_list;
0 v  v& {& o  A/ K  P
# H% g  s  W7 g$ ~/ P#define va_start(ap, parmN) (ap = ...)0 r( r5 r3 @6 T1 v/ f( m9 }
#define va_arg(ap, type) (*((type *)(ap))++)# [; M: l+ q) z' z; S# f0 ~
#define va_end(ap)
- N- O6 k& u( p1 [7 G5 x#define _va_ptr   (...)
+ _4 v, u+ l+ L6 y8 X#endif
# X" C; d% a# Y: }( X# j+ l  V- u: @' C
以上为"STDARG.H"的内容.
) f" L5 e, A/ }9 p8 e: w' o该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;* S2 M5 u: P4 d" Z( ?
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
% I) U% r2 _" p$ D4 BparmN为可变参数的前面一个固定参数.
" P9 h, L) d, o" Y8 g7 e  D4 Wva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
1 J; n5 G) M( y) [0 Eva_end(ap) 结束可变参数获取.
; ^) V& j: ~0 r# Y0 F
9 S! z8 V3 U6 O$ `3 }3)可变参数的使用实例7 L4 x: N$ M. Z0 O( S

% p; f% M. p0 k! {实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.- s  d6 M- N( t2 d4 ]& X
2 Z6 l) W' q3 l# I4 i2 \) c
#include<stdio.h>. \1 j3 ]: y# d% ^) N7 g& V' ]
#include<conio.h>( ?( i4 s8 O! m  {; _
#include<stdarg.h>
( s* G" d$ W* j, e/ M- a" f* Bvoid tVarArg(int num,...);/*num为可变参数的个数*/
0 u. c: w. r8 I, Xint main(void)- s. `/ b8 e+ {
{* D4 ?% G: D% {
clrscr();4 o7 y; I" X7 n9 ]( m- E
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
7 s/ ]2 M! P# r5 J% i: D8 JtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");4 U& G& B( `# h( F/ }, ?
getch();8 R' l, v. O8 ]3 R6 u7 b+ [+ ~
return 0;
4 w4 k0 w* b7 J2 V# a1 |4 z}
. y; Q( T7 N  }$ ~" rvoid tVarArg(int num,...)7 h5 q3 ?( g7 V* U7 }% M
{/ G- L5 L0 Y  ~% S- Q' }/ f; G
va_list argp;  /*定义一个指向可变参数的变量*/
/ o' Z% b- W1 N8 W0 I8 \6 e, ?va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/! S# q/ o  J0 F4 a3 k
while(--num>=0)# Y( K* r) d3 |( X0 O4 U+ ^
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
  L: S7 w+ C0 _. _/ T5 T    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/3 z5 L  T4 D% ^: V6 O2 a, K  S$ g' n
va_end(argp);  /*结束可变参数获取*/
9 m" L0 I4 ~0 ~3 z/ n. oreturn ;
' m; L, v  v9 L}. E# ~* E, H: W, ]4 ~# O- ?
- S% ~. H* U( w; J  y: m- Q
4)可变参数的使用需要注意的问题2 \2 ?2 s3 E* O$ B3 m
, z. }& b1 Q  @/ {2 P  T
1.每个函数的可变参数至多有一个.- d6 A) W1 k6 i% i3 Z. ~* v
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
" x6 q. \2 C  E! Z$ j3 E8 P3.可变参数的个数不确定,完全由程序约定.
" a- X# Y# O: d  I; d, t4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
$ v+ ~5 ~  u; Z而printf()中不是实现了识别参数吗?那是因为函数 , Y4 E" S2 R+ F; F' x$ o
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 9 R% g( O, V$ t6 s% A1 n0 l# e% s
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
* d! Q+ ?3 n5 M' y; A过在自己的程序里作判断来实现的.
+ g9 g; N- w. [4 ]7 n& J. ^5 Z2 U) v# g5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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