捌玖网络工作室's Archiver

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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
l~Q6DLM t'p#BB 它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.4j*Q7c&y,uB)H
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
5bsk}]YC0x 定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有Q*u6Lly
实际的名称与之相对应.
JR VG9UiB;i 由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间. IY$ldd(Y
然而,更多地自由,同样也加大操作上的难度.
?g,S;Y(D p^p 以下就对可变参数的几个方面作一定的介绍.
6Z0Q-v UOY;p
u(D j3hU)V-c:t 1)可变参数的存储形式.
H"hlg7VPw
7NY4F)Ug1t)K3O 大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
Bb2^$YjyT:d/A 存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
f;Bv"d-[r,p8~2r h3p 在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
%v)nAsQo!\o 这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
{8Lb(I T8\Fcu 因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):y7Ee*w$@%]7ih
栈区:
P)c jT'c P6` U"Q.x zrR4l
|栈顶             低地址
\1G$R*J? }-~(~Zn]!}
|第一个固定参数var1
0XeFxR |可变参数前的第一个固定参数var2
6[']v0@`l |可变参数的第一个参数*c^0@ Kz
|...
j-fmn@:x |可变参数的最后一个参数
&]8R2^eXK |函数的倒数第二个固定参数var3
1T$~Q W,G*\2u"d |函数的最后一个固定参数var4
#PC:gxR a |...
)aX9rH,b-{(?,t:s-r |函数的返回地址
Ol#r-u-iX |...T1al#o _tD&Ni
|栈底    高地址 Uj'WT;w)ziB
5z Q [v/y;u
2)使用可变参数所用到头文件和相关宏说明wgjc LSz
#U{P M.K
在此,以TC2.0编译器为参考对象来说明.bi&j([w
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
4Y/HIMlZT M4L CY H/~ 此文件为:
+D?#ROL /* stdarg.hBx,[ ?w,z#[6H

%N#h:G]jq;j4q#BK-Ua Definitions for ACCESSing parameters in functions that accept
0NT'km2tve a variable number of arguments.?]m-?Nxx

z'M+nA9\Oi}*q Copyright (c) Borland International 1987,1988
"z1PQ}9G All Rights Reserved.r8U9CAzJO
*/
(e,gB q)yY5[/B0P #if __STDC__ FE [8lU
#define _Cdecl
+Ns$VE%@ #else j!Q^ |Pt:h
#define _Cdecl cdecl
"]Rf0Z`fe3T #endifF%o&d&{S6l(tlnk
3}6i4W5R0Q#lx
#if !defined(__STDARG)okf5~y
#define __STDARG"K5\'oZ-N$b

Hu'{4}n0\S)G typedef void *va_list;
6A5rnK~*{&Or
RDhQ*J#q,o #define va_start(ap, parmN) (ap = ...)
0d/Zp\] H:KP7sW #define va_arg(ap, type) (*((type *)(ap))++)$N&k |;s9t uB et-fn
#define va_end(ap)
k,wAC2SO2XH~.e #define _va_ptr   (...)
%r^qG&H1N:v #endif
5dc2f%N.g^:We ?] Lr A oss1?
以上为"STDARG.H"的内容.
/{?4Pl5J2^ 该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;;J*@W]"?qU!V3Wf'b
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
k0QzM-z6d WQ ] d parmN为可变参数的前面一个固定参数.
7{LzU3\g va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.Fyrn`n
va_end(ap) 结束可变参数获取.
:wr"b Wq;r.w
c1oSC+E$~LiC 3)可变参数的使用实例
&Y W"~'q.O/M*H{ %j ]"W {]_:V bfv
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.m } D$~-t0~N,Y
lAn9T;e
#include<stdio.h>
/@y[*K dXw5G1_6L #include<conio.h>1rzP:R@ D:N}@
#include<stdarg.h>
.mK%n/w1ed+K;^k void tVarArg(int num,...);/*num为可变参数的个数*/"L7w5T:|r"Y*xomKy
int main(void)
1ec] pMt {
KCx3S&[c/v| clrscr();h,hk/K A,G`:NZ
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
3QLHx0r8F tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
6{5T,raw9]xl getch(); o%yh0r&z"~
return 0;#?8z5{3w3v
}
(eT"weDs void tVarArg(int num,...)
4ZY8Z8Q D*e {+Yd jLAV:~&^1d
va_list argp;  /*定义一个指向可变参数的变量*/
Wq$F&j4\fT va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
x"A ]w)Fgb while(--num>=0)
9S|\^%B4B/C   printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
*f(^:}c;bRo b     并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
-LTHez.?3B H va_end(argp);  /*结束可变参数获取*/
ct-h$Gtk9s V,W)U-`y return ;
iO;wRJ*gk }
?o sWD ^5Gxj%x4J+xX
4)可变参数的使用需要注意的问题
;c @"LqF_
+K&h/C-Ru+_P6n 1.每个函数的可变参数至多有一个.
5w$^n:o8g)Nh 2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.)?0w[+z| ^g
3.可变参数的个数不确定,完全由程序约定.
6VabS.r:ot ]U%\ 4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.hf }vw/{:E
而printf()中不是实现了识别参数吗?那是因为函数 fNkc*C1s
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 6O ?%ON+\/@f
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
TJ,]{C(S 过在自己的程序里作判断来实现的. o0Gyg~P&qO
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

页: [1]

Powered by Discuz! Archiver 6.1.0  © 2001-2007 Comsenz Inc.