捌玖网络工作室's Archiver

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

函数的可变参数详谈

可变参数的英文表示为:variable argument.9p3AU[O
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
,e9H9D K7[/V;s,T"C 可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不#Q ni2^:O}
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
P2~.r1oT$J4^$d 实际的名称与之相对应.
M.]t/eu$jJx'oy 由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
9C)x b f1u@]}F#f 然而,更多地自由,同样也加大操作上的难度.
_-ELZ)ZQBt 以下就对可变参数的几个方面作一定的介绍.1o OY`*Mh
3L4^q*W.DI2SA
1)可变参数的存储形式. fjx`8Z-zY
$J*L7B5Lhc5E U;G0r
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
,gY3^5Vc.R8X)m 存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
8A'[ q8O{ 在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
2]$]e~q[ d:Bp$Q 这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
2m!W1C] s#cw(x? 因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):;y2Qg-n b
栈区:
\u#r!B;x7r\
U$^ zR,W#hk |栈顶             低地址
} [2o;rSU%^ dnT \[Zg6y:y5d
|第一个固定参数var1
:~F wO\Y:g |可变参数前的第一个固定参数var2
7k*vW0K]3H4`6b v |可变参数的第一个参数e(x H-Kzo#K6Y B
|...
Q-M"s a8l\"c]-G!v1y~ |可变参数的最后一个参数
h{Y'ZI2I |函数的倒数第二个固定参数var3
3g)C/u-?*St |函数的最后一个固定参数var4
"M2xq+pu O"L |...
n"M2h|S y,n i0p |函数的返回地址!g-U:rGbtbRC
|...
o u~'~!CQ |栈底    高地址
%w:t[ T1iM l5qW;G"^W&H
2)使用可变参数所用到头文件和相关宏说明 `wE3X5f|
_(\$G}M0rW
在此,以TC2.0编译器为参考对象来说明.0f@O }f L XE#G
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
5|A8|"ZA9Q 此文件为:6x9|8DW b
/* stdarg.h
Zc W%{0Fc%p0bin
G v#@ { Bch Definitions for ACCESSing parameters in functions that accept
@^i#E| a variable number of arguments.;DR:@2z c
o0[h/M2[A)F5P
Copyright (c) Borland International 1987,1988
Zg*m1_)C7E$f0n All Rights Reserved.
| u9C;i-k */
yR±w d gcC;e #if __STDC__
\&g\Gw[x j.KT #define _Cdecl
+M;y$u'USL M #else
\3J_| `)OZ #define _Cdecl cdecl8g-r8Yp |h `Z
#endif1S!f5}#w q4x }1Gm2A
_gr3\jaa3q
#if !defined(__STDARG)xK|xK[c
#define __STDARGHxVzm4s
C$Z5l0hY#Gl
typedef void *va_list;I;s-Y*`Ys`D
sfc$X3fV
#define va_start(ap, parmN) (ap = ...)
!bV$_5@ E l@2K$I #define va_arg(ap, type) (*((type *)(ap))++)
7G[;vd'r#}QT #define va_end(ap)
1L_5e7a#X #define _va_ptr   (...)
~g.A|0S/Z6My4h%z #endif
/I3No ` O#OT/z B$W3?3b k:Ft]y
以上为"STDARG.H"的内容.
6uI3N3Z/n_4M Y 该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;@d%U]zR(T!U;bj~[
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,%A2i3RR]of~
parmN为可变参数的前面一个固定参数.(Iii8y0~ HE2l
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.z9v*U.kaa
va_end(ap) 结束可变参数获取.o(I-N|} vR1@M
)a1k.SLHHA
3)可变参数的使用实例
_5HF d#}
.I^1^?lt;J ` 实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
1gQ%O#f0z1kR6WF/|b
_5H(~,m$j9|~ #include<stdio.h>e;_D!ka2]+qdI
#include<conio.h>
PNt3S0lU #include<stdarg.h>J6`V,p+PY h K.f
void tVarArg(int num,...);/*num为可变参数的个数*/
fd+JfG int main(void)
{8?0p:o|U*q {)OW&|fM4lo5CL*w
clrscr();
1MJ~mA tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
2oW"NO? g4r tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");)aB \;b L0^IS
getch();
%FL'_6m7v5p%T return 0;
2}*`0hg'u$F l }
b:TbT1x@;J,x8A8R void tVarArg(int num,...)W5kP7Krt!R
{
4{9TkJx,k'| va_list argp;  /*定义一个指向可变参数的变量*/Jb.?x)l
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/_ YmFuA7A k5iN&WR
while(--num>=0)
_ RI9DYP~   printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
FT(D8x-] K)J     并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
SD"GL5Q7Y va_end(argp);  /*结束可变参数获取*/
!Z.^ vjDofy return ;dLo0_@8U|
}
qo8S|lx(R-rSn
&M^ rOKK4hn+~ 4)可变参数的使用需要注意的问题
.~u1c@ op$I;SW Q.j\8yo?
1.每个函数的可变参数至多有一个. WxW9t5?8jXH
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
#ni{` pU 3.可变参数的个数不确定,完全由程序约定.#D3@*](?l1lJ
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换."S,I'\/Zc#L
而printf()中不是实现了识别参数吗?那是因为函数
NQ(Q(?,prL~ printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
5v{E)u R 的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 &{s]w-m4g
过在自己的程序里作判断来实现的.
_M['ycqU 5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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


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