捌玖网络工作室's Archiver

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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
.c'Bf0Qs8b!_Z 它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.m aTK0\ Y)^V.Z
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不Kp+{}6d+`[
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
%\@+w2L(I"o.U;j@b 实际的名称与之相对应.qo]*{(L:s+R?
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.f1q0SAt4^8D
然而,更多地自由,同样也加大操作上的难度.
-y@#G-mZ+}U V m$P 以下就对可变参数的几个方面作一定的介绍.8dv4F:]5S0Ds c

s3w W+fQ 1)可变参数的存储形式.Q~WT$z
;]rZZ5~y"d!V)b&y
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放, Ne$a/Dbi
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
4n8u kQcu4d 在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
(WW~;g"k4tZ:J jo 这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.9UvH0mXG#{
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):'DaD)^+oG ~
栈区:;AVtB,l [+J-Z
*Ro0R;u_ \U
|栈顶             低地址
0v+L5y ?ko_;r b
f)`p o*I+i+W O |第一个固定参数var1 _t8Mh!A
|可变参数前的第一个固定参数var2
8C9Y3AB.wseBX-[ |可变参数的第一个参数
IhcSha/L |...,S5W5a)z)fbD!`
|可变参数的最后一个参数&\jM$r"a#u
|函数的倒数第二个固定参数var3
'racc SG*vF1jl l? |函数的最后一个固定参数var49w.b U5g%K}
|...(z0g#ux#{,H
|函数的返回地址
|0N6RP:L_3?~ |...*VQ0{uU
|栈底    高地址
|GN#t4dB:j.E;Nq*W
1sdEx+f(b 2)使用可变参数所用到头文件和相关宏说明
~+J4i`1o,B;? ,O jmO0cd9]g
在此,以TC2.0编译器为参考对象来说明.VOF_!E
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.]MRq A
此文件为:6R L*^x v(LF
/* stdarg.h
A$R M)R8t Y(c 7Z%u1s7cv j6o
Definitions for ACCESSing parameters in functions that accept
+r$f+R#P&d0xW@v a variable number of arguments.
)Z3mr:k q&u 0RY'j5D!m$B%z
Copyright (c) Borland International 1987,1988
$Le5P!|~` s&R1j All Rights Reserved.;ei(\,_h3l
*/-a~/s*\ V.I
#if __STDC__h.M gg#]?3X(L/Kd
#define _Cdecl
+GI ]GP-ln #else
A.CR"F.y.H6D #define _Cdecl cdecl
R H:~,f-N #endifA_%s"m%Kq0xo o
&c3gb-b @O3|iy
#if !defined(__STDARG)4A8o6wV^
#define __STDARG
/z?U/zL nwX/s
4I-W,ee*uK%r typedef void *va_list; }&};S8hnp

]*Tpk'X6b #define va_start(ap, parmN) (ap = ...)
h5Div_F] #define va_arg(ap, type) (*((type *)(ap))++)\9i ^!a#Q3JVu
#define va_end(ap)I}3_ fJ`
#define _va_ptr   (...) PSQ5M V!Np
#endif
2?O'?rb
"V}/T ild$x 以上为"STDARG.H"的内容.
qa c'c)D;Fky 该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list; b;M0]wwC
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
7~M"cX,Y^8E,_sB parmN为可变参数的前面一个固定参数.8e^?w(H,h}XE
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
a%l)F%?0D&p qj va_end(ap) 结束可变参数获取.+n'E&v'RS
Rm ZJ*hO/Q
3)可变参数的使用实例7D/d*{)g4xt

.CE TkAl,V 实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
F|f|GDoS]
~+aC;AFQ.JD4ah #include<stdio.h>*Uf*eQ _nI``
#include<conio.h>)G r5I.d j!Y
#include<stdarg.h>
)N.`7_'L!GE^ o M&I4x void tVarArg(int num,...);/*num为可变参数的个数*/W0? M1gMjr
int main(void)
CQ+K&`*S4^ {
-uWp U3u clrscr();"q7z@rO
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
_-gB5Q.[ i tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
)[sA8}+q/R^\b | getch();
@:f+F5M"aWF return 0;
\4M!X(tJ9x },Yb `4G9Jg@3A C
void tVarArg(int num,...)
@)_+I8O?v*i0Jc {
~'VPy|a#?P va_list argp;  /*定义一个指向可变参数的变量*/
7j&i J4sB}P va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
^0y|q&@ while(--num>=0)
Z3p+|8ZV0xG$M   printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
^Pm"oRZ8Y"C'F h     并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
MJt|5] p va_end(argp);  /*结束可变参数获取*/
{,Fn%E"@7c&G return ;s LrH5LP,{(JJ
}
{T.cR#Q-cO0v!ZK
}6Dl Ff7GCC 4)可变参数的使用需要注意的问题KE,_n9w;`

7Z0rX"X8t.od;[,{3~ 1.每个函数的可变参数至多有一个.I T}Ck0m4W/Rr
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
U_k y9L? 3.可变参数的个数不确定,完全由程序约定.5UE}3?x5W2XR
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
RD0pG1N 而printf()中不是实现了识别参数吗?那是因为函数 vVg@?y @8yb
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg enX rsw:eG
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 4r*s;Z8B n S_
过在自己的程序里作判断来实现的. 9n,E-[!?.eM5ys-Z&X%|
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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


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