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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
3 H2 j: N% d" _0 z$ b4 U0 H它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.3 U! Q1 y  q% [( x& \$ M- b
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
8 W% j% b6 k5 R1 ^# u) ?1 {8 U; e7 u定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
$ _4 q, q( B( g) |9 Q实际的名称与之相对应.6 m- n4 S9 K2 x, G! O5 G
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
+ a. Q: n, K3 x9 F4 w然而,更多地自由,同样也加大操作上的难度.) V& ^* W* Y- u+ P( `# `9 y  V
以下就对可变参数的几个方面作一定的介绍.
! _/ ~% e3 V& Q. `0 n; r. M, A# ]: P8 e, U6 ~- c& d
1)可变参数的存储形式.
  y. i' ^: b' q& i, U) N$ d' i$ T" o& d! C% d% F
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
+ h( p" ]/ S  `) l  }存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
# o1 N3 H' G! o  ]4 C- X, [在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
% Z2 Y& ~' B2 D2 o8 G; E这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.& l7 H2 s1 r2 _. y4 c6 C# q  n1 ~
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
( E0 [6 B8 Y$ H+ y, _栈区:
5 I0 b" I, s& w8 s5 O2 l, `8 Z" ]. M9 i3 X, e* U3 n! K- C
|栈顶             低地址% `, U: w4 E: L4 f5 L1 z
( q% O; y, d& x) F3 V3 ]7 x
|第一个固定参数var1! w! M/ E# c7 Y' H7 n* b
|可变参数前的第一个固定参数var2* a1 g" |2 `. ^
|可变参数的第一个参数9 l2 s  w4 p* x( r) p; N
|...7 P; l3 N( n. }! C# X
|可变参数的最后一个参数4 c2 ^7 z  C' e( R2 e" w  v" P- {
|函数的倒数第二个固定参数var3
( m& ~0 Q7 r# y9 T% H7 c|函数的最后一个固定参数var4
& E' I7 C9 p& `|..." ~- u. X$ A2 p4 ^. N
|函数的返回地址3 l# u* Z+ k  `! W7 f
|...
$ V( L# V! n/ L+ x( z4 E|栈底    高地址
7 ]4 |1 t: G$ e7 L! `2 O' t3 ~1 Z" V* Q6 j
2)使用可变参数所用到头文件和相关宏说明- h7 R! H- I/ r7 ?9 j/ ~2 X

) w0 O6 \; \: i4 Q, ^在此,以TC2.0编译器为参考对象来说明.# X& C# k& W+ U+ _1 I7 M
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
8 ~4 q) O  W& o- o  I- [* r此文件为:" D7 }' q7 x  }) `2 s5 K. |% c
/* stdarg.h% L/ V4 D, s' P" _/ @: G" E

: h0 B) [' U6 V0 f2 S6 [Definitions for ACCESSing parameters in functions that accept
( L, S. h* m6 \3 F! Pa variable number of arguments.
/ T7 M* P- s4 Y5 x; v% o$ }" S0 r  L9 v  W# D$ f5 A
Copyright (c) Borland International 1987,1988
* R8 W/ f1 |: m' k/ w7 {All Rights Reserved.  A! O5 J! L9 _/ P: F
*/. y( Q5 m) s. P, H8 \3 b, q" E
#if __STDC__& K4 k" Q1 u! n1 j& h; `; E
#define _Cdecl/ i8 l$ g" i2 \! z5 [5 Z: m
#else
7 X% _- F" W, G' r: y7 Y#define _Cdecl cdecl
3 M# p2 m7 A' S* Q5 j4 x0 I#endif; l+ l1 `/ V7 W( ~* F3 M' }
6 G7 [# [8 e' P& s) I1 o
#if !defined(__STDARG)
) M- Y: x4 ^3 _7 x#define __STDARG
0 |5 o( c$ o  G: M% z+ \
$ O+ r& E4 r3 V& }typedef void *va_list;+ C" v  ]; M) o/ W; V8 A8 k. a
/ e5 f; O* p- k' Q  x
#define va_start(ap, parmN) (ap = ...)
: r, s( n/ K) Q% g#define va_arg(ap, type) (*((type *)(ap))++)
3 R4 x- ?3 g0 J6 X  i6 b#define va_end(ap)7 S* m! p! d% t" z* p5 b$ L+ W
#define _va_ptr   (...)
5 u% U& I3 l3 B* x- ~1 {4 y2 u) Y7 X#endif3 A4 h% A+ k1 K# h
7 K) N% ~, |+ M5 \6 }2 Y* P0 \4 U
以上为"STDARG.H"的内容.
' F1 S7 M+ H: G) [* l该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;; ^' c2 n0 V! w0 K- _
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,, k+ Y' S* t) r, {$ [! k0 ]! f
parmN为可变参数的前面一个固定参数.+ X# @0 g$ d/ f5 D- v6 H( F8 @1 ~
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.3 ?+ Q1 r# H9 k0 O& G
va_end(ap) 结束可变参数获取.
3 X' _  k* \# p& T4 K
1 w5 m" \7 z) ]# l9 i3)可变参数的使用实例' T/ n+ V6 b; z6 Q% |/ j

( O0 T. Y# o7 c+ W( r; |9 w/ ^& F: w实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.$ V: S2 j5 X+ u& u# M

! I$ C. C! C% j. {- @) v#include<stdio.h>2 H" t+ {4 t+ T4 {- y8 @
#include<conio.h>
0 F+ h; K. Y( C9 K3 k7 e: o; J, i#include<stdarg.h>/ L5 c; x! a9 J; {. u% O
void tVarArg(int num,...);/*num为可变参数的个数*/
2 q! l8 G% o* t; ~int main(void)7 }% N& K3 p( N6 d9 `1 {
{! Z7 z: g$ m: Y8 T9 v) C+ c+ r
clrscr();9 {9 {1 i3 @; W3 f* {) ~9 R7 ]; ^( @
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");# O% h. ~0 u8 f9 F6 a+ k
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
6 _6 Z. p5 a/ Tgetch();
5 e. O' o& \: b! v7 Zreturn 0;" ]" d+ {7 D2 L- z: ^! C; J2 \, N8 t
}
( L5 x+ E0 z8 L; Vvoid tVarArg(int num,...)
* {7 u' t4 o1 D; K{
1 {: m0 Z3 A; F9 V, P4 c8 Hva_list argp;  /*定义一个指向可变参数的变量*/
9 }# s  o2 O8 K6 X# \; K! Gva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
$ n: I6 K( O! p$ e: p6 J9 Xwhile(--num>=0)
$ r$ a6 e9 f& W( \  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,- D" B( n5 B0 l. k
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
4 K9 O+ q8 w7 J5 j( ~3 Y4 \va_end(argp);  /*结束可变参数获取*/: v; R" {: {5 Z6 r* K' c
return ;
8 e2 v) O3 ~" ]5 ~+ T! s}6 x- C' U5 o" c% ?" j9 X& s: l
( o5 s8 M. D( |6 J/ L4 h; W
4)可变参数的使用需要注意的问题! \" i8 h* R" E( z* ~

! O( r7 ^& b. ^7 s) f! f2 r1.每个函数的可变参数至多有一个.- J( q6 g1 L" ]) ^/ q1 n1 {
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.' [5 _, n+ }8 o3 u; G
3.可变参数的个数不确定,完全由程序约定.# P2 ]5 ?* a: S+ Y$ S8 z' d
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.; a& U8 n5 y5 R5 e% y
而printf()中不是实现了识别参数吗?那是因为函数 ( K6 s1 S, g/ W& v2 k8 K
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
' y. o/ _8 i( ?8 u的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 , M' n6 P! M% [; o* D
过在自己的程序里作判断来实现的.
3 _( b; k2 Z$ s* V2 ]5 P+ A3 {5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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