捌玖网络工作室's Archiver

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

函数的可变参数详谈

可变参数的英文表示为:variable argument.0Qp1n.|6~5T b0w
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
%g1H dA$qdgJd R!G 可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不 [:oc9n+U^7lz,~
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有M8TrI {#e#s;w9B3d
实际的名称与之相对应.
?k/B/^d:`!o.A.s 由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间. D P3m"HQ9_0o$GB
然而,更多地自由,同样也加大操作上的难度.
],dO+V9Wu&L-IC 以下就对可变参数的几个方面作一定的介绍.
\P;NM IS 3D dbKF3KAx@
1)可变参数的存储形式./uls b1Ju#U

Z)^Y;_W 大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
fNt1yW$V.v+O 存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
Lp G6vszn~l-n 在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,Gv%t3CwD3L Z
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.s1VV1]6e
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
/A*vQAMf'z n 栈区:
2g^6GE@nb J
3qX\Y)s1u&hb |栈顶             低地址
Y\o,Q$L6yA \NvQ!P
|第一个固定参数var1
m&@f G @f2s |可变参数前的第一个固定参数var2
zz FSv y |可变参数的第一个参数xn_VDg.^W7Q
|... O#}i&nCm)_
|可变参数的最后一个参数&uq'H-V6Z9rI M zM"V
|函数的倒数第二个固定参数var3
l8z\&n8VK a;l"__ |函数的最后一个固定参数var4wiL zr.v;X |6ZF
|...:o(JX:LP~q8Tf/o
|函数的返回地址c5XAAy;o*P
|...
3r.jF%B;A | ['l |栈底    高地址2f:g)s6U7v6W{

nv-GM9yBY P!H5l a 2)使用可变参数所用到头文件和相关宏说明
&b)D]o h/\ }U%^5j"i@!A5a%n
在此,以TC2.0编译器为参考对象来说明.
VD/k-z^gr){ 可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.XYZ {ALst |
此文件为:8w-P qE6V}(k)?
/* stdarg.hiz)`6xrKyQ

Y5g1m/vpZvX Definitions for ACCESSing parameters in functions that accept
#v9KC4L~}5m&_ a variable number of arguments.
"[#O2A"L7zHp(_ s 3[@.g9SZY*Z G
Copyright (c) Borland International 1987,1988%kg!o7Z,Ik6u2S-vI6MZ
All Rights Reserved.E n3^,V^
*/
c{H.@mWc #if __STDC__K w2zr I+`Bss-v.h
#define _Cdecl!J5H$Y%~X~_n
#else6u_HXu%xy
#define _Cdecl cdecl
%G1x6e9iJJQU2W #endif
!q&Yo tq7`3I bK
mQ;G9\A~N6p#? #if !defined(__STDARG)
}5_tH#E` #define __STDARG
*\p V o)p%`
E5E{6NK-\~_l typedef void *va_list;
#l \B"P`?
ayAi4co'q+[ }} #define va_start(ap, parmN) (ap = ...)|9|\US iq
#define va_arg(ap, type) (*((type *)(ap))++) ~*Yp AU3@:\#N
#define va_end(ap)
&?)V/c] FG #define _va_ptr   (...)"EtT}@&r3F3mw
#endif
%{\Rdlt |@u2uGc3Z%w
以上为"STDARG.H"的内容.
I*r'H MT vWB%C&B 该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
l2W4I;|X,Q-Y va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
dZ'Y:k(hRx(L7i parmN为可变参数的前面一个固定参数.
4dR'}SH-f5Y va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型. I ~'GmP)S*vwuCx
va_end(ap) 结束可变参数获取.2?f[Oy
5zs4y2Ym9NS5eg
3)可变参数的使用实例3O#h2A([;doE6j

8q^wt7fH/t 实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
,an&D1_Ft)j Z^E
.h:OiE\4{ #include<stdio.h>
ym+ttE"Ey #include<conio.h>-kF~6TcLK`
#include<stdarg.h>8D,S(B,K,z(ri(~ ~6ogc
void tVarArg(int num,...);/*num为可变参数的个数*/_t6E:v7W"f
int main(void) ?)??!`x+q/M-|&^4X'OT
{
ehF8~+i7KJ o(G clrscr();
,KJ:qG@'a#Xn} tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
9QVu{]8e@-h tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");m"K ^{8~n
getch();)x&an\$}J6z
return 0;
1U6@5E.WC#k-E }_7Y3SymT0h R
void tVarArg(int num,...)U"C6tKb%_(d5sT U:t#f
{
0Is~%H+a@|X va_list argp;  /*定义一个指向可变参数的变量*/}XBn#RX%Qt9|M5j x
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
v!f@%@D[/f0r~ while(--num>=0)o#`{'`-c
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,'BY-L$`-@)dv/a,J n
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
^ q,N(A@S TlCo va_end(argp);  /*结束可变参数获取*/ @8Z I]]H)tP
return ;
5p#c zx6V }
0ak3t,hM(L s mx v4_Z5l
4)可变参数的使用需要注意的问题
"B9BFgFm KR kf6|4o*Lx
1.每个函数的可变参数至多有一个.
e i@2j:ro 2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
_&humCN'G 3.可变参数的个数不确定,完全由程序约定.
9?,bX G&W`NvHy0ar 4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
Q n`M$`o 而printf()中不是实现了识别参数吗?那是因为函数 i O}"Im?
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg c'@ xGdN2x3]
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 Wc)wI;c-u
过在自己的程序里作判断来实现的. }9h*D2W{6z&]k.|
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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


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