捌玖网络工作室's Archiver

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

函数的可变参数详谈

可变参数的英文表示为:variable argument.9c&w LA&sW$d
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔. Y6f5J+{s:V@n
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
-D0[)v+r7~ JuO 定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
~/Bky"Vm"L 实际的名称与之相对应.(oZ9k v*l1t_/b.Ys
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
"bWf5O4r:C+G 然而,更多地自由,同样也加大操作上的难度.|+Ii2I{$M
以下就对可变参数的几个方面作一定的介绍.)O {0o+T+nW%r,o
V xC%J-y)|^f6[9h${
1)可变参数的存储形式.*Kk*}C2C[
+D-^ Z7`rT vUeA
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,!rV"ZFE'c-Md&x
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区. z;M_-Z&A
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,~`U'i{
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.(G v_8fK
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
vU)V `KE,g T*v&{ 栈区:
6i nDW)M%j "ce8G8M8_kP$ON
|栈顶             低地址
3EB#bI}%V @q5U%i
;Rr+qauP;_.tg6gO |第一个固定参数var1
7{q)K @dX*r |可变参数前的第一个固定参数var2
6_h`G:[3v] |可变参数的第一个参数
d w0X!d\3x a |...a}+~-ZqZ
|可变参数的最后一个参数%^HY[ ~C`b
|函数的倒数第二个固定参数var3f"eS v[FF
|函数的最后一个固定参数var4
et(Bu.p |...
OV(pmu$m `g` |函数的返回地址D#W5L1aNF
|...
Q2_itTU I&\^P |栈底    高地址#S1{B%e-Z:^9NhH
(j;KN6{qX!hE's
2)使用可变参数所用到头文件和相关宏说明u`3YE;YXg3Hd
4j7rMB({ac3d
在此,以TC2.0编译器为参考对象来说明.
:I]e cbZ[S | 可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
B'\ kO_(~z 此文件为:
jE/SM9Q /* stdarg.h8N Q2E(o T
[\1@4y k!p.Bd F-]
Definitions for ACCESSing parameters in functions that acceptq DYfe N@"e
a variable number of arguments.'k z`.};o{e
v ueB*u}(W%Y(N
Copyright (c) Borland International 1987,1988
4KK8K XlFf All Rights Reserved.C@2w r0Y
*/
-o!dh3A;nH?V J5kd #if __STDC__
j?F,a8|Q'd #define _Cdeclj[ |xj-b%q$n)?t
#else
!w#]2G,GN #define _Cdecl cdecl
6d)yCUE [ NO!A #endif*{1`0x!u*k|T8aq
g!K d&J U
#if !defined(__STDARG)
Cf^)udRoE~ #define __STDARG
"^bY]2y)h5n /Q;zSq9["_
typedef void *va_list; oa6X w4tR{"Bf
I*P&O#Tk3B2mtsW
#define va_start(ap, parmN) (ap = ...) v])k.M9{1sx(z
#define va_arg(ap, type) (*((type *)(ap))++)
b%x A `&y V #define va_end(ap)
LL5wK8z_"W0|:I #define _va_ptr   (...)
*i~C1Z+n #endif'HSzLU aaWb r

kmoyq-]iL 以上为"STDARG.H"的内容.
f Tuw5w 该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
xT M|*i.e(jn |)z va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,2XA3fcO,Hv
parmN为可变参数的前面一个固定参数.
#I4m-O2V$L1Wh"{z va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
+Fp Za'OSY@,{ va_end(ap) 结束可变参数获取.
+E%UVL5R c?3R(~*FYM
3)可变参数的使用实例
J4MPs:JF Pr AR"]&| x$jA
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
y%S!Tg7dLWv
tP[@6N:kRlb #include<stdio.h>
\Ae\8J~+I #include<conio.h>6_0? r@M`^
#include<stdarg.h>Z+O[.v%q.}*i
void tVarArg(int num,...);/*num为可变参数的个数*/Z TD9F+~[(Wo\ P
int main(void)
']:l Rd7tq*U`:? {
v[p-WXdr clrscr();ZQ]-^kQl ^
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");3xMNV9Hh
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");M:_7r#Kh x
getch();-ERE{:gc+pN
return 0;
cvg%FuQD oX }
?G!m]S7V1U+^ void tVarArg(int num,...)w9H\4gY)P;u%B!e
{
!zK*`m&}o%a \3a va_list argp;  /*定义一个指向可变参数的变量*/ u*A$Y]2U"`
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/ K6t1l,I+h$]5["j@X
while(--num>=0)
Ww1o"f&p yA9Y   printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,'l*t]6fN$@-C
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/j~5R3`-\8|V6q3P
va_end(argp);  /*结束可变参数获取*/4W-G7c7} X1s7B6b2`,wT
return ;
GM7g.~A a!{z,F } _'FK6h x&_| D6n'`
5O.yph$JP9w[r
4)可变参数的使用需要注意的问题
6Z0U6j:F^7u#t.vD
M dK7eJ M'\ 1.每个函数的可变参数至多有一个.'T4D kz AlN
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.1anZF&rC!r*{F
3.可变参数的个数不确定,完全由程序约定.5S:PYHP"u2e{
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
8W)Hrs;?1X 而printf()中不是实现了识别参数吗?那是因为函数
5Oa:O&`6qjX printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
K|$Z@+]zMh 的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 -Z,Pie2l
过在自己的程序里作判断来实现的.
H,i/v xC9]#F1K 5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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


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