捌玖网络工作室's Archiver

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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
3c'?2G8V}p 它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.'mZ7L nl
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不V(A+{uR-A
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
]thuA'f.r)x'X"yF 实际的名称与之相对应.V#v*W"r _ ^ l
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.9w3U&c5f"GC6U4C
然而,更多地自由,同样也加大操作上的难度._-e-Ddu
以下就对可变参数的几个方面作一定的介绍.$fga:fV)n

k*w5a ?Ef2wP9p[ 1)可变参数的存储形式.
Y3B4wJh,U"m _0E\
%qyl'k)X*\E? 大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放, r`*[.w3T
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.&yUT9yM&p1bO
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
4]`t L#utx9o6p 这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
2c8w-Y,Kq"Q|5E#~V 因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):n!@!Q7oBT
栈区:$jj g | vix
0e+m}nOgU/q6C'k
|栈顶             低地址'D0Z\H8D]8Y
#Sjk]BG
|第一个固定参数var1vq-T*FD s"O\
|可变参数前的第一个固定参数var2 u"mL'Q;P(T%f;w
|可变参数的第一个参数
#y~l-b3gJ6_ |...
)F9qtA+g |可变参数的最后一个参数
\%kUT%{%T |函数的倒数第二个固定参数var3
m O3j(?oJ6Wt v/` |函数的最后一个固定参数var4
CdXun |...
7mw R}M2kXP |函数的返回地址8G'b-nvV+x7X
|...
6VDqw xG(S |栈底    高地址!w ^^&}|
y&Ny&YoR0e\
2)使用可变参数所用到头文件和相关宏说明F qU;@7q1yO

9T [$wI)ed/w,]7^(jzB 在此,以TC2.0编译器为参考对象来说明.6Hb.s*kimj H
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.5\i:Q `/{ o+HQ8Q
此文件为:0HA0^&KR4nTgI
/* stdarg.h
5z J2gCf-['a
4~F2R}Q R%VgPm Definitions for ACCESSing parameters in functions that acceptI8gb(QMGNlT
a variable number of arguments.
fU+Vzd%\/yl%CC ~;uF.z3~1x^2G Ken
Copyright (c) Borland International 1987,1988
hoE;mF7K-W T+D All Rights Reserved.(fnI:Q&_6F
*/
1WiWzjX)Z #if __STDC__'Q @(]z&_gP
#define _Cdecl+GQ9e,R)x7S
#else
rfN+^f!Wt:Df #define _Cdecl cdecl,U-M'MD ER!S-H3{;D"g
#endif
#j H+x-M!J7Gq 7R8d`~[ g%Z2{B
#if !defined(__STDARG)
$Iycbm8~{ \ #define __STDARG$c,\/K^7nVh&Q!|
@;Q8i%qa'|T
typedef void *va_list;/a@hb;`,kE;f

] S ^@(q,s #define va_start(ap, parmN) (ap = ...)5C(_!MD }[O
#define va_arg(ap, type) (*((type *)(ap))++)
&}7AW5] fn\sBn5h #define va_end(ap)
8PF3z aw~_ #define _va_ptr   (...)M,nQ6uL+K$^
#endif
DK8Td(\%BJ;z\%F,U"| EnE a Vm
以上为"STDARG.H"的内容.
:EE4P!u gW&z 该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
S:VBaw1D5_-Hp!V va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
zn:h4gZ parmN为可变参数的前面一个固定参数.
M[:k'Fp4b}5? va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.iqpc;GH
va_end(ap) 结束可变参数获取.t^e#x AF0K

kr6['s)OQ 3)可变参数的使用实例Z k,oO:AZ8J
g"qb8[6bN0[
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
d,f:VL7}:M&y I
Nfx&|&c #include<stdio.h>
[ iPG1s o2?/B8_ #include<conio.h>2X0w^ ^9n7x1C
#include<stdarg.h>6k }h3GSug$o
void tVarArg(int num,...);/*num为可变参数的个数*/3h4}F U.@ Mo
int main(void)
jFV.I Wos+fW {
I9j s,KiAO clrscr();
2T'|0X,W2zg tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");t9nCeby_x o
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");Y4M{ {q?h;cL
getch();
a SE!R eyZ ^t return 0;
:Hn }jiMaN }
;u%J ue.JT void tVarArg(int num,...)H#ApzEp xJ#vB5A
{!jWA!KCjq;{
va_list argp;  /*定义一个指向可变参数的变量*/
(?$v'R$zXe#]&aS G pz{ va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/'S$l4B;}V
while(--num>=0)
5n3\x6D$G$@4s   printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,-D5?;f9O\ l0s3~
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/0ucyz;am
va_end(argp);  /*结束可变参数获取*/^Q?cc.hx,D
return ;4]1kc @h
}
V_3Ti.v'h*~Z D W0Lv'g*Ic5k
4)可变参数的使用需要注意的问题
L/o9L'df3Yk
v2^ E NY z 1.每个函数的可变参数至多有一个.:m8XzP2Hs
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
W/^dW0^ Bi 3.可变参数的个数不确定,完全由程序约定.
qM"vk? WyHEY 4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.+q\+H2TT)|^
而printf()中不是实现了识别参数吗?那是因为函数
i,L,c\a printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg HfOVrV xn
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 U|0t n7Xh
过在自己的程序里作判断来实现的.
5b0G4X!@tK@] Wl 5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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


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