捌玖网络工作室's Archiver

zw2004 发表于 2008-1-21 19:42

函数的可变参数详谈

可变参数的英文表示为:variable argument.k7^&yG-e[&y
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
j%C!VsV ZO'eW| 可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
%\JI1P4pMnE 定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有SD;h8I$J
实际的名称与之相对应. w7| wAx1_#@8N
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.7M%KF J2Q,B2]'}
然而,更多地自由,同样也加大操作上的难度.MHp1dp-B
以下就对可变参数的几个方面作一定的介绍.gI5J]C4` ^8DQ+S

(~:x+\$M8tLB 1)可变参数的存储形式.XAJ`~u
$j~Q ]s]$na:`9e
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
0OM9S:I;O1B 存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
0`W8C~nL?#bB;f 在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
l`[} y 这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.a5qbsr
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
V9x`3a9\8{'OWb 栈区:lw[a.r8j|n

p%mz+e&kB(OWO)F |栈顶             低地址
-y\p%mZ
CX2p!e\"x |第一个固定参数var1
$JqB'sN0Vc6@ |可变参数前的第一个固定参数var2 `1h-| e]8z,f
|可变参数的第一个参数
9Q%`%UU%`sL0u;i |...
]Z:u{ } b'y |可变参数的最后一个参数
}d)T/StH8h |函数的倒数第二个固定参数var3I'{x4c4J-{_#`8u'B
|函数的最后一个固定参数var4
0N*\3gQ bGD%}T&{K |...t%f p#[{c*}!`7Z
|函数的返回地址
VW2Q0w2c/T4LY(h(z |...
i`t/Q w \&u} |栈底    高地址
&J4WI*PI%}&F"y
'pk0yTT)b es 2)使用可变参数所用到头文件和相关宏说明
$yl1JWTj [#Ti
S1g!}$u:U^6p 在此,以TC2.0编译器为参考对象来说明.!B;iqxI
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.uo;}F'BMXkWa
此文件为:
7f_}-Z7S7B3b /* stdarg.h
.\8W+G hRHa%}r y
ql3fK R Definitions for ACCESSing parameters in functions that acceptA,sc |S,{,\5Y,Y/]
a variable number of arguments.?%m;y-|1jg

P+P+HRS,~^ Copyright (c) Borland International 1987,1988
lvOr(Ke All Rights Reserved.8yfb2gBX5h-_
*/
iatB&l,|c+[ #if __STDC__
wzv:rKw8JVzi #define _Cdecl@th_Vl3rX}
#elseK%nO5lG9Hm
#define _Cdecl cdecl \(zstV{a
#endif
'_PK)SB#O t5} {j | Bw U LV8{q
#if !defined(__STDARG)a2P^XB:A
#define __STDARG
W4e3\RIUwU:S $}rsZ'BF%}
typedef void *va_list;
fdnB\l `
oto:RyL #define va_start(ap, parmN) (ap = ...) eq.G%ST#t(@ v l\
#define va_arg(ap, type) (*((type *)(ap))++)+k1HEI LWf AL M
#define va_end(ap)@}#VB5E9RTA
#define _va_ptr   (...)&uXI Q{IS
#endifX7t B4`#Ej

ZWB1\2Z]Im 以上为"STDARG.H"的内容.%EMt:B m;f~ D
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
i2Q$t4M!M$_!E va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
/vDs#TtB e*F parmN为可变参数的前面一个固定参数. D-ol}!@
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.7M8hFY/H
va_end(ap) 结束可变参数获取.
Er/y(t ]6?'`7I,E"m '~} u&Q_ p!C Gg*|3u
3)可变参数的使用实例"x@x)f8I*xo
O[yT1Q
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.bV;@1J3E
\$[9h3d[Ju6RGJ}
#include<stdio.h>d#Gx1{R;[k
#include<conio.h>
+VI-a+c-w.K #include<stdarg.h> a] Z.i?
void tVarArg(int num,...);/*num为可变参数的个数*/
Bck T GUu? int main(void)#DPZ wMT2\5\3~
{
4BA0lr(C ZF clrscr();/yPk"q&oUq
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n"); m q{ A#gY1h4[P
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
6p iW&M Z-KW L getch();
c$b$[vCRz return 0; t7d'D:`s LN*Z*Y"N%C
}
U7xm0xz1m{ void tVarArg(int num,...) r0v ~e2a"} a9Az
{ e vUFxi%pv.`
va_list argp;  /*定义一个指向可变参数的变量*/F7u1V6_ m.T
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
WVLAi9iD(@ while(--num>=0)
W$_ j[;oX G`   printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
7T iUh1D-X     并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/;TIs;e9Y.x7}
va_end(argp);  /*结束可变参数获取*/A8V%wm:scx
return ;
*~#FGv`*f&s3k4l&o }
r:`~;Hf
9hH!eaw_ybL 4)可变参数的使用需要注意的问题
4N8Cf3O:BC"[
.E*^-Zu8U,up/^ 1.每个函数的可变参数至多有一个.
sd`MS"|Y O5u+j 2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.b|2\z0eV
3.可变参数的个数不确定,完全由程序约定.d,_*AY R9k!y
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.3l&ncm^,{p
而printf()中不是实现了识别参数吗?那是因为函数
}J#E;Lm2x printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg !Go? [`&L)\5RW
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 hyYd EpHGD6b
过在自己的程序里作判断来实现的. ~R"H"x+oA.\
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

页: [1]
【捌玖网络】已经运行:


Powered by Discuz! Archiver 7.2  © 2001-2009 Comsenz Inc.