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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.: P' ?7 c% Z- z1 j, Z4 J s
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.3 h9 e5 [, ^1 o' p
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不( A/ v+ f) S/ n: }8 P3 T8 t
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
; r* g6 J& x3 R6 ]实际的名称与之相对应.* A! P3 P3 \0 X) v
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
9 g( C, |- T: d- |然而,更多地自由,同样也加大操作上的难度.1 [* K' N) Q! o4 F
以下就对可变参数的几个方面作一定的介绍.1 B! K5 ~! `5 i$ |/ X+ _
% Z8 d% R& z: p2 a5 B$ L& ]1)可变参数的存储形式.6 I5 }% s8 x4 O* M
+ ^4 N+ r1 ^" G0 T
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
. K! E/ @. E2 W W$ J" j存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
, `8 P l, U: M$ j1 ]在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,( X, E) a3 X! H2 ]5 @ k& r
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
& K9 V0 J {' Q2 C# w0 Y因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
3 z/ J$ @5 l/ u" L2 \& y* ]3 S栈区:
3 ]* A: `1 t1 l* {2 {* z' O
; K/ V% ?. `3 k; }' d% z$ X|栈顶 低地址$ ]0 ?; q3 ~) a8 s
9 a2 h, B& u8 _& } v5 X0 ||第一个固定参数var1 F! \4 a# A" g' P7 O* S; y5 Y
|可变参数前的第一个固定参数var2
% S6 }9 S6 Y0 _ g+ |" Y- C|可变参数的第一个参数8 ~8 a% e! H8 x
|...
9 e7 y. J7 @+ ~8 v* o, L0 U% U9 l|可变参数的最后一个参数7 w/ B+ r8 U8 |' N
|函数的倒数第二个固定参数var3
8 h7 w$ O! r" Z4 a; I/ v* p+ r|函数的最后一个固定参数var4
- c% @5 W9 i( ?8 X|...
( \- h: U5 U8 [1 k ^, C$ _1 t, ||函数的返回地址
% K. L) n5 Q' [! w0 X: H( S# C|...
# P4 T% a4 W" P7 Z7 C, C% o- w|栈底 高地址
4 Z4 j9 V, ^* O6 T/ y0 u, ]5 D% i. [6 n T. V8 C
2)使用可变参数所用到头文件和相关宏说明
k+ r4 M+ V0 W- ]
. H5 K1 V6 k2 C, L/ D在此,以TC2.0编译器为参考对象来说明.1 k6 Q1 n$ u$ Y
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.5 d3 h$ ]6 e; Q+ T1 d
此文件为:2 J1 h& c6 _- h# C! Z0 I# x1 {
/* stdarg.h
% g- ^7 }! a; x* B( v
- F* ]' l9 d9 S5 o6 L: G% ^Definitions for ACCESSing parameters in functions that accept
0 E# }( n3 l$ K; Ya variable number of arguments.* M3 h2 Z# u* E
& l( E0 E, Z& M8 O6 P& o! kCopyright (c) Borland International 1987,1988# T9 {4 o- x. v2 c6 V2 w2 P4 k
All Rights Reserved.6 T- ]' i6 [# h$ L
*/, A( W/ w; K1 x/ u( o2 x& E
#if __STDC__- e8 c5 `3 k# {, T3 u( A% k% Z6 L
#define _Cdecl) Z' `9 N2 u9 A1 k# y: Y
#else% y( t N7 Z- l* |
#define _Cdecl cdecl$ y: g$ i( ~" m. T$ B+ _5 i
#endif
. Q U4 b% A r* {* `1 E& @1 i, \, v$ _1 M2 b4 O7 C
#if !defined(__STDARG)
& _; c f, z1 X) H6 y3 r5 f: r3 J#define __STDARG
1 A! w! ^5 K" R0 L5 w$ v
8 ?. H7 M3 V+ {/ a5 A/ Ltypedef void *va_list;
: A4 `5 o @- a ]4 N% m8 x& ^% g" k! V/ F- T$ _
#define va_start(ap, parmN) (ap = ...)0 X# u8 C4 S! X2 ], X- f
#define va_arg(ap, type) (*((type *)(ap))++)
1 U. y m3 w4 V5 r8 b, J# |# q! [#define va_end(ap)
" g7 O, \* {! Q#define _va_ptr (...)
- Y! ^* E( }4 X9 k4 K3 A) S# f#endif* h# W- t: F/ k8 v9 F) k# D+ M0 \; e
, s. y' W8 {6 R% l以上为"STDARG.H"的内容.
2 ^# d5 e! O% ~; l6 |0 I该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;% b' ]) g/ } k* |' a
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
$ _/ v. v$ w8 W/ |! R& pparmN为可变参数的前面一个固定参数.
1 u) u0 y( \; Y3 x L: wva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
) | N/ E, j3 U+ Rva_end(ap) 结束可变参数获取.; Q' f9 F3 o" ]# R N* b
/ l5 P! n3 j3 {8 H7 s) ]$ f3)可变参数的使用实例
6 P/ ?8 R* W j% m, F
$ T3 Q+ R& n1 p, g# P5 x实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
6 [# \& m: t- E3 Z6 `( i8 s
' M) s( ^0 k; X. H& [# Q#include<stdio.h>: }4 r5 K" W9 Y: A
#include<conio.h>
- _; K+ l7 A7 [; C! D0 }' ]#include<stdarg.h>
9 ~2 g) P* z0 R: Mvoid tVarArg(int num,...);/*num为可变参数的个数*/" d+ M$ i" d6 [8 `% y1 `. L! e
int main(void)
4 G6 r' }" z; p% W{
4 D+ X* J, z* ]* uclrscr();* r4 H9 j) I9 O/ U- ]% y7 w
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
2 ]# `' r; R/ y r3 v# @" K6 gtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
& i. t9 u& g3 z0 `5 P" ?* r5 Rgetch();
2 P% {1 ~. X) l& s2 F: U- Kreturn 0;4 U) g ^+ n7 U' }$ E
}( c5 A! W' D8 V
void tVarArg(int num,...)/ T9 ~4 e* w3 v* U3 J6 ?! m
{
y# v6 q1 N( b0 w3 iva_list argp; /*定义一个指向可变参数的变量*/ q4 E+ T5 E: L) h' R" Y8 t
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
( {7 B) R7 w, Rwhile(--num>=0): N+ ?3 V5 R* T; G2 E
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
' c8 I. X& {- f6 ~5 T. p. l 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/6 g" ]' Z4 v5 v K
va_end(argp); /*结束可变参数获取*/& ^% d* I. G7 p& ^' W! R+ \
return ;- v3 U3 d0 z1 V$ Q8 E& L0 a ^1 v
}! d+ Z |4 ` c. a9 m
! i: d9 B( I* k0 d, ?4)可变参数的使用需要注意的问题. N! |7 F# L) j- P- T
3 u1 U' u, b2 r/ k1.每个函数的可变参数至多有一个.5 [6 P( {( Y( g8 H/ n
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.6 C" k6 Z) G2 ^8 p5 Z2 P' l
3.可变参数的个数不确定,完全由程序约定./ n: H8 H! Q$ t' T$ k; r
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.# q$ K/ v) i" @7 W
而printf()中不是实现了识别参数吗?那是因为函数 6 b& _' ?; p5 g1 `+ n
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
% {# H+ S. t' X5 L i+ _/ M的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 ( m' _1 `4 G! P, ~6 h
过在自己的程序里作判断来实现的. * r, M0 ~; X2 [( ?: g; [8 N: F
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|