捌玖网络工作室's Archiver

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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
.z%d\We-` 它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
Gom7`;e(~Pt;Q 可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不7P hR)d fvq~
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
X2A^f)vx+`$i\\ 实际的名称与之相对应.d3U:p$vk)c-S H
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
6H5uP9mPM 然而,更多地自由,同样也加大操作上的难度.
\^V*\ K/z*E\(e 以下就对可变参数的几个方面作一定的介绍.
jHx x$jgF1\
-~f Q+{cm8AhX 1)可变参数的存储形式.M/\o.n;Wb(l X$L
'q;T1b2~\
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,4_%[y0F'`F
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
uTmB6y/i4uq K;c 在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
K!g~9YXTC 这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存. k2D g?!L@
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):r-kh0M u7n$F.X&J
栈区:/ZX[&i)Ot
A-D z0z Pib*\0Yu'w
|栈顶             低地址 \B(xsTBnO,B[
_?RpQ8k0mt1E"t
|第一个固定参数var1R7S%U/B[3Ux*] n v
|可变参数前的第一个固定参数var2J"]%L/` y^Ql'O
|可变参数的第一个参数:VK)WS h ]
|...bY [cr{
|可变参数的最后一个参数
~&|&g"}!T |函数的倒数第二个固定参数var3x3`7\NM
|函数的最后一个固定参数var4
Kd9c(Db*l/C |...)c$@#bRjc
|函数的返回地址
R3u ^"x,Btw |...
+ZS&Sb7g4{Dc6d#y |栈底    高地址 CZ;D2]^VS

y)T xa;N#hCI~ |-v 2)使用可变参数所用到头文件和相关宏说明
kDH5u ^zt
T2mN.o\ ~ UKT 在此,以TC2.0编译器为参考对象来说明.
h,ea"M\[(Or&G }J 可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
/zLD`o1\3h M 此文件为:
b0[I(L\W Jh.[z /* stdarg.h
s3[~ KO
Z gv'S*|$Oq}PE{y Definitions for ACCESSing parameters in functions that accept
Y][5f^C_Ni a variable number of arguments.| wc0[ F S
'g _f F"I(X;A/@
Copyright (c) Borland International 1987,1988
5P P+js*aB All Rights Reserved.
@2XX$Bdh */
s)[1C-U@"["y.m)y v #if __STDC__9s{X.@Fj v
#define _Cdecl8DS$G|:WP o
#else
1xiga#N NPBR #define _Cdecl cdecl(dt jfrg
#endif
1gl{ W GB\{VA P o2G7h)CY u t
#if !defined(__STDARG) LT&v,\+HX^B
#define __STDARG
y`mw^&l !E.|1n+\/S(?
typedef void *va_list;
X)i,Ry"i M
q D V-l2Fd,dR #define va_start(ap, parmN) (ap = ...)
i$C?~-B8Ya #define va_arg(ap, type) (*((type *)(ap))++)V'y0kGX:[%X
#define va_end(ap)
S:v*~n?|Vh #define _va_ptr   (...)E h%E G'F#]R{JR
#endif
Y'bnYWPjm6dR _hF c6K#|2o%\
以上为"STDARG.H"的内容.
j_j'e9Dj#_)V1z 该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
k.wKX,x`W va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
K c-@c8u/yh parmN为可变参数的前面一个固定参数.
:f.g!j_ZW#u va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型. kf7l+{N%tk8D,HK H
va_end(ap) 结束可变参数获取.
9F|l*Nwm#E
Sj*m(Df4N!`o k 3)可变参数的使用实例
pNi+m5`0?aVO |"xlB3~omO hq
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
hb]8~DW#t5i
l5FlB:y #include<stdio.h>u)a}#kRA h
#include<conio.h>
-CF0E5A0CoH #include<stdarg.h>y5dmAID2I7~
void tVarArg(int num,...);/*num为可变参数的个数*/A&kli ~5NWK5q
int main(void);[j9v?O]%KG
{"aAfG(`T+{:G6T
clrscr();
iR;ad NzV tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");+bwe9ZE&n&L'L
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
0t3SX*S2D z5W-G(_ {+Z7[ getch();
_v-bKw,R;z return 0;SK-t;E6z/k8` V
}
t0q@`8GhW L void tVarArg(int num,...)
3UZ"T8k:I {
R$gP)f@ hNx va_list argp;  /*定义一个指向可变参数的变量*/|L,C+aC
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/CWw7@0{
while(--num>=0)YX.G5~%V vU7u
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,+hU&no,p$C@dG
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/0k"OY%F0[#F
va_end(argp);  /*结束可变参数获取*/
my;L?3|/V return ;
k8ls{waG\5bq }o1jKn7h'H

^$~/N~#i5T S y(R5\ 4)可变参数的使用需要注意的问题
k\)d&v-CWk
s;~W @J 1.每个函数的可变参数至多有一个.
3R}'A-S%I/P.^ I 2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.4k7F-~E7u:o
3.可变参数的个数不确定,完全由程序约定.APH\E pau
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
{,}r;`;R$nD'{ 而printf()中不是实现了识别参数吗?那是因为函数 2Oxba%[+b}(~
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
b6P$K ae2\ 的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 }b#\Z1c
过在自己的程序里作判断来实现的. $nV,HR@"]J
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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


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