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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
( ?( t& o1 m" ^; Z它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.8 q; A& N1 c- I- ~- W3 p; I
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
5 \. H( ~4 d& }. d定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有5 d, I% v0 J- @; }8 W8 p3 t; N5 f, E
实际的名称与之相对应.
6 P/ Z9 R% W8 f9 I! L8 ^由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
/ G4 _# z- u% w* l+ x然而,更多地自由,同样也加大操作上的难度.
2 G0 H5 r5 g: ?/ d以下就对可变参数的几个方面作一定的介绍.
[& \" S' U5 F( P8 L# K
* ^& m* b; P: B5 ~1)可变参数的存储形式.
9 H1 I+ [/ h7 C2 |+ i1 m& N
% f3 t2 l9 O b. A大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,1 p$ ]" V; O/ E/ |- t
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
9 N2 M3 u, ]3 Z5 g$ d在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,: |! j& o/ C2 \/ n8 s: A
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.: Q' m; R; \. S
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):& [$ ?$ B5 C4 X' m0 k' X
栈区:; A. A6 }. z) U7 L
0 J5 D# t7 @1 m' {* ?: C: d|栈顶 低地址/ \$ e' D+ l# i) `
9 r& [1 c# t7 d' i) w( D|第一个固定参数var1
2 @8 K9 c0 Q1 Z+ [* p|可变参数前的第一个固定参数var2, g( ^- W- w2 B& c9 S8 w& |' o
|可变参数的第一个参数: h5 w( Q, k- r `. i
|..., l$ w, P8 A6 {
|可变参数的最后一个参数5 O f7 p5 k8 _4 ^5 Q- E
|函数的倒数第二个固定参数var3
* u. S2 g: x: `9 f, A0 y|函数的最后一个固定参数var4
. Z( D4 @3 ]( k# h|...
- k7 [4 D' I' v0 h: O|函数的返回地址* [, |; I( l# ^5 P; l
|...
6 ?6 u" h( M2 X% U8 }" b9 H7 G|栈底 高地址! @% v7 x4 T1 ~7 \& d1 X, D1 W
P* T% Q, ~$ |4 a7 I- m7 P% ~- w4 U
2)使用可变参数所用到头文件和相关宏说明. ^ ^! E+ q2 A2 H: x' Z
' V) X; ^0 }( J. F& ]) N% a在此,以TC2.0编译器为参考对象来说明.3 R+ _/ [ v/ A; c7 ?9 e! }6 k
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
! }6 g- {1 n* ]/ N. I! Q此文件为:2 b* ~3 {7 c: }; O1 l
/* stdarg.h
9 ?/ L7 F. H: g R ^
( {4 t/ }" Y6 Y0 R mDefinitions for ACCESSing parameters in functions that accept
$ w4 u1 c- c2 Fa variable number of arguments.( o3 t, `% M' O6 C! \' q) u2 Y
& A* L6 k# I9 L; {/ P1 S7 a2 ]Copyright (c) Borland International 1987,1988
0 y3 \* x! w$ X3 ^All Rights Reserved.
9 e3 y: y4 B1 U5 s0 r1 S*/
9 `1 A! {- X4 c& d- t+ ?#if __STDC__/ A9 K2 H. D& I5 m% g
#define _Cdecl
. H$ O- @$ z- C& \: Q0 m4 Q#else
) _/ s o( v9 t# s# o3 L) q#define _Cdecl cdecl9 L6 m! v: U3 s, Z
#endif& }1 A7 f# M8 S1 n8 h
3 a' g% \2 {2 m. o#if !defined(__STDARG)/ o. L- R; C& d& d$ @- x
#define __STDARG6 t; [3 q' f# a6 {, r2 |8 h& O
0 u8 {- G( i; L: f0 Y. A3 h/ g
typedef void *va_list;
1 R7 t5 r; x# }
; r+ p+ y8 g; Q% s5 B#define va_start(ap, parmN) (ap = ...)
/ j# h4 S8 g) ?1 A L8 t#define va_arg(ap, type) (*((type *)(ap))++)
4 K! w, i1 E* J+ A# o% N#define va_end(ap)# o4 s9 j" O' b
#define _va_ptr (...)/ J2 x, ]# O" I5 w* H
#endif, t) \! @4 ?3 D3 P
( D7 F: S; }" H5 V: F
以上为"STDARG.H"的内容.
3 D# |0 F% k3 K+ u; G+ T3 E( q该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
5 B9 _7 S+ \" V4 O$ |* Uva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
5 x7 Y/ A6 {$ g; ^6 c$ uparmN为可变参数的前面一个固定参数.1 h/ F: x |9 \& a% u6 ?! _
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型., L! @: D( r% A2 K; Y
va_end(ap) 结束可变参数获取.
2 o# C# }* Q/ {! p% \1 _- f/ X( `, K3 U
3)可变参数的使用实例/ h. [, p+ K) i! F+ g
9 h7 Y' { G. ~
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
8 @" G5 q' T. {# w. }2 i1 ] {2 X6 X! N3 q5 n
#include<stdio.h>) J/ `$ T; C6 `- p6 v/ u
#include<conio.h>: l( k5 T: s5 b$ q& Z! `8 f) ^
#include<stdarg.h>4 \6 A: Q1 v. b8 |, i/ p: O0 x$ v
void tVarArg(int num,...);/*num为可变参数的个数*/& A; }0 i; _/ d6 \
int main(void)/ `7 T: r+ j4 n+ M }
{
E+ ~5 N! ]- h, F! \: j' |clrscr();6 t2 O1 n. o6 _! L
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
, X8 {/ \4 d% u1 @* LtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
* B1 @( }3 M7 e* qgetch();2 Y8 o( e2 l" e0 {6 J: A; M* v
return 0;
5 l: A/ P8 O3 k, ], v}
$ @, X. G0 g; M& D: w _" }* \2 Yvoid tVarArg(int num,...): r5 A2 s Y# |! t9 q
{$ a4 R+ ^$ O% S, E9 ]. x
va_list argp; /*定义一个指向可变参数的变量*/
0 z$ n" m- O+ d# T" G! R+ Nva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/# N9 J5 d1 s' d) p/ O- x4 z1 C# \2 t
while(--num>=0)0 @, p3 o l7 S' Z
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
3 |- H: L) K$ J' _3 N 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*// `6 p* n9 m+ t$ Z6 \% `! J. j
va_end(argp); /*结束可变参数获取*/ @; u: ^3 l# R5 D% j& I2 r5 s
return ;) K$ O7 q( k5 [% A- ?3 F+ \& A
}, T2 N! e% H9 x% s2 u4 E* x
2 N$ Q1 ^% m( k
4)可变参数的使用需要注意的问题! e/ ~0 j1 o; C6 I. Z6 l
/ u2 j0 Y, |9 O- x6 j' y" V( {" g8 g( t
1.每个函数的可变参数至多有一个.% h5 C9 i* e ^ t" g' B, I" E4 `
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.' w9 w6 u! m( z+ t
3.可变参数的个数不确定,完全由程序约定.
9 k, O5 d; k/ c: z5 \4 E; c$ X- f4 o3 Z4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
4 T/ [" `$ A$ d& Z0 N而printf()中不是实现了识别参数吗?那是因为函数 ! n- @8 B4 \% N) W
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
3 `! ]! X+ `5 t8 R7 R6 T的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 ) Z( }4 Y7 v- \7 _" t+ ?* o
过在自己的程序里作判断来实现的. ! Z$ ]+ h( c& M: Z
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|