捌玖网络工作室's Archiver

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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
;O|9K,yX?K 它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
4h:j lkJ$\ 可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
G(iN"_1]^K.AQh 定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有7s5` H{mul t
实际的名称与之相对应.
3Jj$X1v!K8TQ/d 由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.6V#{-Z \Z.]
然而,更多地自由,同样也加大操作上的难度.
K?/M/fv"@ 以下就对可变参数的几个方面作一定的介绍.
-WBy,e7S$H_L
,@6EF2A.O+LS,pV%j 1)可变参数的存储形式.Nd+]P/V0\

+OO;t/L A/r 大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,Z lzf@M"?A'r
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
1j%Sw4fW,i 在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,.wX'P1\i\+}5@
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
~:J2|H/uE/o 因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例): l%zH"]W&Iz3@
栈区:4z)C/]5mD
Nu&[M'Y`THzA-|"X A
|栈顶             低地址R,\8O:tTr2j+d
t~1P cnvy
|第一个固定参数var1
sU4{2MZ)Y |可变参数前的第一个固定参数var2J&P`M"x`TA L:U
|可变参数的第一个参数
7R$B\$|G9I |...
j!Q3z1KQ6@;~"@Y.u |可变参数的最后一个参数3p4g m~9X:m|2V+L!m
|函数的倒数第二个固定参数var3
f)pysM9`2Fv |函数的最后一个固定参数var4!u9x!l1p"c(yR]
|...
.c f|1\ F |函数的返回地址
@U9U vz&S*Rg-pq q |...
no d lB9LE| |栈底    高地址
3Pq B{6W JT8Y8Ar`
2)使用可变参数所用到头文件和相关宏说明
BU_5a#hNp
'\8sC5n1Cp.r 在此,以TC2.0编译器为参考对象来说明.
C @ P5p l"L&O)Z*H 可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.O2b:y G;Ram?
此文件为:
)?:yW'H5o$M3y_ /* stdarg.hx `7pLq:` n
f(@[$e4@l
Definitions for ACCESSing parameters in functions that accept``@%Rfz!i
a variable number of arguments.U p,ZQo

`PO+cRX Copyright (c) Borland International 1987,1988
]n2r'{^'IP All Rights Reserved.
"DV DWX/L|#~ */] M [ t o%F.{-tG
#if __STDC__
0A%@nm&b #define _Cdecl ST8o9K9J2U5K
#else
S;xhaLxS{-^k #define _Cdecl cdecl#]{'jh0IPrV$z6~
#endif
ss;{ ]L3Q)AR
'E6RS%nh J #if !defined(__STDARG)#kZCN9T7N3~
#define __STDARG
,Vr#? eh(Z A;j0Y 5N2sNb H
typedef void *va_list;r/^+w5|oR t
0S1a@L,Y/~ K p gMB:s
#define va_start(ap, parmN) (ap = ...)4Yo4A~|m)T-dh&e!t
#define va_arg(ap, type) (*((type *)(ap))++)
D-P z1wJU7_'i!d #define va_end(ap) I^/H4{9ft6C
#define _va_ptr   (...)8k%N!Yy:yG
#endif
T#CY\g5Ug2t R
i0s G+Q6R 以上为"STDARG.H"的内容.6d9S-lX txTj
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;$_Wm1y4qk
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,\Knrm;UOA,a
parmN为可变参数的前面一个固定参数.\&s'D5eQ/M{3W:Z0V
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.5Q?| uniIY
va_end(ap) 结束可变参数获取. J*L8H1c1e ur:gWn |
Eu,{0S#|SQzyF
3)可变参数的使用实例
7M"W^ok)U]
0dS!r O\xV 实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.*bC f9q"Dh0u

Q)x9xG7lvkg,C#] #include<stdio.h>Dx~ n!N0kD&oL
#include<conio.h>
Q zea'`;|\ #include<stdarg.h>Koo_-s
void tVarArg(int num,...);/*num为可变参数的个数*/)z(S3P8FW _
int main(void).c9X5lhp3C
{{\(QK0_"d s]/KZB
clrscr();{xrK,a7X.?Xp
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");F w y5cwb
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");+Pv5b"}8|3w~-|L2?o
getch();!t6C:i/R,R6s
return 0;
w#xYB6m4E| }
0c1nW6k{*MA*{ void tVarArg(int num,...)
qC5F],N/{F%m {
J:K,\"K]t,`5LC va_list argp;  /*定义一个指向可变参数的变量*/K4U'Dr[f
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/ d U&Wk3w7cOI
while(--num>=0)+TG#Q;f z*W:~
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
gn3JAYEcWr     并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
#|L+} }!y]1l va_end(argp);  /*结束可变参数获取*/
H E!Y5cQ3_,AYYN0q+GZ return ;
s2n$y(P\ }
.n'Z&ke#r7c {v
!v[c1a-M(vI 4)可变参数的使用需要注意的问题
*iM-p` |oy
'j[`3A$Ehqe 1.每个函数的可变参数至多有一个.kY;@WC*}
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.Iuh SF'k
3.可变参数的个数不确定,完全由程序约定.)oRs${b [ y)S+w
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换."n8Zk-kGL)dkH
而printf()中不是实现了识别参数吗?那是因为函数
nt |y"VI printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 0h-X1@2AylbPO/O
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 $\'{[Dk^V
过在自己的程序里作判断来实现的.
"@S s BEY)] q 5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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


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