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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
" [: J# l% l  T: ?  h. \它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
* M" D0 T- m  C1 D  {4 P可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不* T) o( K: R$ ^  Q1 o# t- ?2 Z
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
( x1 q! A& y. b9 k, t实际的名称与之相对应.
1 h1 _( L5 V* U" D. O# m2 D3 q由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
, n, F& q  T( |0 e5 n- m4 C5 z+ i然而,更多地自由,同样也加大操作上的难度.
" ^% x' l. w% ^" B+ L/ q1 C+ ]) P+ z. z以下就对可变参数的几个方面作一定的介绍.7 L+ [, [4 o, D6 x9 ~

1 u! S, @$ m  a9 F1)可变参数的存储形式.% T. B# I' {1 Q0 q, j0 D" }
6 o  _# y! x" o
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放," `6 P% U7 K) F4 _2 O+ i+ s
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.8 _3 N0 ^  A. q. D0 j/ V9 g
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,- d3 c$ @' ?5 S) c9 n7 ^; C
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
& T8 a4 E# w2 k. v, ^8 ~因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
% x3 Q& z( a3 p) Y$ G栈区:
3 Y( J& F4 n4 }% h/ I, r9 L! h$ z; y; A
|栈顶             低地址# u4 ?: o7 Y4 f# ]

) I4 N( m" S: d9 W|第一个固定参数var1
9 f' n" z- z+ I& ^+ M|可变参数前的第一个固定参数var2
: ~( Z( ]- X7 h" k# D" D. \|可变参数的第一个参数
5 {- q% N1 |3 @0 L0 W5 p4 \|...
3 T; i" }* G( d( L1 p|可变参数的最后一个参数
9 w$ z# h. R) q3 {4 q  V! t|函数的倒数第二个固定参数var3
5 s1 R, A* f3 j2 C& x" ?* ~|函数的最后一个固定参数var4
8 H' e( m: x6 S4 `4 E. \|...
. J- K/ ~; J% p* q1 S|函数的返回地址, s6 S" ?1 G, G* Z* T6 w2 S. @1 g
|...
& g3 r1 [) ^. T- G- t|栈底    高地址. q/ a) s) x9 h$ l4 |5 h1 H! G2 I

7 T$ g0 |+ f( v7 F$ a2)使用可变参数所用到头文件和相关宏说明
. W$ G! y+ g! `- p0 P" c" r5 R; ]5 Q
在此,以TC2.0编译器为参考对象来说明.
# y: I2 y, c! G) Y4 `可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.$ z/ F% C6 y$ X4 Q2 ?
此文件为:
/ _9 f/ \- Y  Q. [( m$ C/* stdarg.h
% J/ o9 Z- ^# I* I3 D
1 X" T3 M$ Y0 p/ fDefinitions for ACCESSing parameters in functions that accept0 Y  [7 W) N! X; x: M. l; C, o9 D) J
a variable number of arguments.
' \, {4 v/ |. b8 o2 l& [
: R$ y0 k* s6 g' y2 |Copyright (c) Borland International 1987,1988
! b+ N5 G3 j! ZAll Rights Reserved.  |) L% @6 a' f* ?. r
*/
% X5 {: l/ A4 m' }#if __STDC__
0 V5 c, d5 F/ ?) s#define _Cdecl/ O4 @6 p3 R- d. T( g: x
#else
! a+ f1 N3 C5 J$ R6 ]% ]$ f+ f#define _Cdecl cdecl
( W( c8 f8 M0 H#endif
+ h& J1 Q+ H  h2 B
' \9 D! L4 X0 @  ]* x# _, D( l( j#if !defined(__STDARG)
. h! p5 f8 Q" {#define __STDARG& f4 {+ W) v2 z- K! d5 g- a
# t! p3 U, {2 i' Z$ j; [
typedef void *va_list;
. t; Y( Z4 g" e4 L
4 w0 G: O- z  u; `7 p#define va_start(ap, parmN) (ap = ...)# m$ z5 v$ S' Z3 z2 V9 Z8 m4 Q
#define va_arg(ap, type) (*((type *)(ap))++)7 V& F" {! r& ?9 \4 ^- S
#define va_end(ap)
7 J/ C7 d6 R' j# \& t/ P#define _va_ptr   (...)
$ @! K$ |. O7 K/ w% m# p+ h7 i& Q#endif7 X$ s4 `* `3 H8 w% J6 C% H
; `5 I1 R7 |) \, U
以上为"STDARG.H"的内容.0 s9 D: O) K% g: l
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
% z0 P; Q1 `$ U$ jva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,3 {9 I8 t7 G, j' q; w; v
parmN为可变参数的前面一个固定参数.
; ^+ M/ t3 u" c( y: Ova_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
! f. w* L: f$ u1 X% V" d; qva_end(ap) 结束可变参数获取.4 o1 ?, f2 Z$ V$ f* @6 ^  ~
5 b" J/ R" b! {
3)可变参数的使用实例
% G& T! C% ]9 N5 v" N; k' u! O8 A# ?2 s3 p% w  O* t
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
4 e- z$ c, @" W: o2 z/ t$ g) ~3 K- v( }" G4 k( i) H5 @# G( x
#include<stdio.h>! {  _3 O! `) B+ q& j3 v% J
#include<conio.h># ]) `# C3 G* W
#include<stdarg.h>
( S: }2 V5 D" }, Xvoid tVarArg(int num,...);/*num为可变参数的个数*/# F$ }2 U1 l+ f  E3 e
int main(void)
/ R& N8 m$ k4 c7 ?! g. l{
6 o- M: T7 l# d- qclrscr();* M- K" v2 X- C6 D/ p; j  D3 ]/ i
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
) b) ]" o. B2 \# Z! EtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");. I! i% F7 h: [# K1 v# [# _
getch();. k3 i) F1 b% H/ b: ~1 |
return 0;4 a: k4 y: f- z+ N: u2 M# T
}
' ]& i% o% L( ?8 \1 C* Q5 @  hvoid tVarArg(int num,...)" L: o" W" q) N* E" s% U; ]
{
+ E& H% \/ s9 |+ K+ c8 Dva_list argp;  /*定义一个指向可变参数的变量*/- A! H, v& Q% }/ C' M8 K/ X- P
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/7 Z3 `3 ~, a1 s) _- X
while(--num>=0)
: d9 F. N: A7 {% _) Y( J# Z% p8 w& W  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
: I' u' ?. M$ j+ z    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/0 n$ U" ]8 W, Z; [# I
va_end(argp);  /*结束可变参数获取*/; F6 H2 {& `! u
return ;
- l, g7 l& j# m/ T3 \! N}
# Z: z# U, H" K8 t; y0 ]: r7 R  L- C( A4 B
4)可变参数的使用需要注意的问题
" x9 h0 i# G: l* h$ U7 u- }) i1 p) R1 j5 W! Q  ^" G
1.每个函数的可变参数至多有一个.& a# ^, s6 O" @8 L  J7 O
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.0 z6 b% T. V5 h, y- I1 G/ \/ p
3.可变参数的个数不确定,完全由程序约定.9 R% r, g$ X3 X  U  Z
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
6 ]2 `' f8 d: `- ~5 t* J1 ?而printf()中不是实现了识别参数吗?那是因为函数
  b' z# T) |$ k5 oprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg % H) \8 ?' J+ z- i: I/ S* T
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
( O. ?+ D2 @5 I7 P( x* l4 L过在自己的程序里作判断来实现的.
2 S; r- l( J1 P. ?5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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