获得本站免费赞助空间请点这里
返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.
! O$ `3 e* d; G! z# E它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.) r, G$ W0 \) X4 L
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不' a/ c( }( }# _- ^1 r7 o4 W3 N1 N9 u
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
/ D7 a. J/ s# D1 J2 E2 V实际的名称与之相对应.
/ `0 Z8 f8 L# N* m! [由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
- q! x3 K2 d8 v2 U: ~7 M& c& u6 m然而,更多地自由,同样也加大操作上的难度.
% ~; x* U  X/ L9 Q5 D" ?以下就对可变参数的几个方面作一定的介绍.
% a$ P8 K: \5 N& B/ M' j9 W1 D# ~" b, X
+ f) i2 z- F6 @$ c1)可变参数的存储形式.. j5 \, ~/ ?- _4 I; Y& Y' t! s( ?# b

" Z7 o/ N/ J% M# t" z' U# p大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,, V) Z! v: i% i
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.. M8 p, n- f2 u. P( p
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
/ j/ {" }. r0 K) h这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
7 \' }( k0 j0 O. J9 W  r因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
3 y3 V# Y2 A7 A1 A% G+ j) Q+ |栈区:# I2 R- p8 b/ b9 [
* n& g) `" N7 j  l. v& K
|栈顶             低地址7 S& n! z  `' S: ]- t3 m+ N; y
! z) `" B3 s5 [; @+ H
|第一个固定参数var10 q  \& Z6 d9 X* F
|可变参数前的第一个固定参数var2
! ]) g7 }- o& ?9 @/ p' I, \: s* U|可变参数的第一个参数8 |" t2 X" G6 E
|...
! m8 L) g9 I4 [0 S3 M|可变参数的最后一个参数
+ [7 _. L5 R- L/ Q  X|函数的倒数第二个固定参数var3' C$ f8 M6 |, ~; s( x1 |
|函数的最后一个固定参数var4
0 D, S4 L" @9 H3 B|...& p3 A. U9 Q2 E* g5 ~
|函数的返回地址
9 x; J1 ~1 ]) K) o|...
8 N  I& R- x4 x9 n* l$ q|栈底    高地址
9 _" f6 @, Q/ H- s
" y) ]2 G" y. x. [  I7 F2)使用可变参数所用到头文件和相关宏说明# S& p4 q% z1 }. @7 t' h+ }# i6 s

6 ]- O/ C  ?' p% ^% M7 |在此,以TC2.0编译器为参考对象来说明.$ a. e4 j+ [5 c2 x. S0 R7 |
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.) c  o% ^5 e4 C9 ^7 w7 v$ ~! y; {
此文件为:
2 @8 d1 l; Y' c3 w" h, u1 J/* stdarg.h
% [8 i% y! p/ {( n* ^# y. {
# }# h7 `' t2 K! p$ y# bDefinitions for ACCESSing parameters in functions that accept
( ^0 G( |, J; f: ha variable number of arguments.  Z' D. I! L7 t" t9 Q) \

/ E7 b. [) W/ J2 C! rCopyright (c) Borland International 1987,1988
) b" E- a- F! B0 w3 gAll Rights Reserved.; i5 j& l; M! q# q. x! V/ [! c1 \
*/
* x% N0 E+ S* k3 s7 o, @4 \#if __STDC__3 S7 j+ O/ E: A% L
#define _Cdecl4 T0 C# g$ ]5 {
#else
' G6 D" a& I. I! e! G. Z; w9 m6 C#define _Cdecl cdecl
, W5 l& e5 o6 ?% I#endif9 {4 l; s" v' a$ |* g" |
6 ]2 u3 V3 O$ P$ B, E8 o
#if !defined(__STDARG)' j. }; {0 s5 M( q
#define __STDARG5 h9 T: }& w! ^8 d* x5 ?8 T
. o3 X- l) {& g; i) ]) o# @
typedef void *va_list;4 @2 ^- l" w7 {  J5 @3 ]
6 H/ M. i$ d9 {- g& h
#define va_start(ap, parmN) (ap = ...)
# {7 q# f# `! V#define va_arg(ap, type) (*((type *)(ap))++); U$ Q, `& U. M* H4 u1 O
#define va_end(ap)
$ @- p0 o; w: E' z- Y#define _va_ptr   (...)
3 ]2 H2 Y2 l$ Q# W#endif
0 ^7 @; y) q! l. J
( t4 |" a; L/ ?( E/ V以上为"STDARG.H"的内容.
5 \0 I5 w- {$ j: `该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;/ e/ h# K3 r7 T* y7 S# o
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,+ l! p  W- T# U' V, B8 E  c
parmN为可变参数的前面一个固定参数.
& V& e7 U2 E+ W5 ?6 x: x& ~va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.( r" \8 v8 _: L( F
va_end(ap) 结束可变参数获取.0 X3 b4 U- `+ u( Q2 |9 {# ?$ y

; n9 _! o; U2 g3)可变参数的使用实例
* A: l6 U: p0 f4 j* B# t2 p1 _! r' D
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.0 a9 J' C4 S/ Z+ ~& g( I
/ I- ~. g. Q; x, {: T' V) \
#include<stdio.h>  k! g1 V7 r& H1 q9 [7 F
#include<conio.h>7 ]) Q8 j0 T. Z; T0 H$ s5 O7 o
#include<stdarg.h>
0 w/ H. e( y# M& cvoid tVarArg(int num,...);/*num为可变参数的个数*/# @$ |) A- b. \8 H3 u9 |
int main(void)  ^/ v; s( Z* j% D
{
* D) U' s  u  r, m0 Bclrscr();# l# z1 p  j: C
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");) L9 R( t( j  ^0 a, u
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
$ t' p) [% v" h% O" T4 q" vgetch();
! L+ r! J) W* i: ^return 0;
! E6 H4 w& Z1 `% e8 g4 s. J$ ]}* d$ S" [6 a  ?! q7 d) ~
void tVarArg(int num,...)
% |7 d6 ?8 u2 T6 h0 C" Y, }- F( g8 l{
, w; V# H! X% c$ f! L, o& ~- Yva_list argp;  /*定义一个指向可变参数的变量*/
1 q! y# D2 v/ b8 {va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
, d! M7 V6 U3 [while(--num>=0)7 U; b5 j& R; \8 H' Z. M. g/ ~
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数," {0 M% a; V+ f
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
5 k5 u6 z& C. ?( ]! Vva_end(argp);  /*结束可变参数获取*/
# E1 ?2 G2 y* L% C1 |return ;
! E( s! x+ v* E. `) J$ m}9 S  i$ \$ D: n
! x: N5 s% B: j2 M" C* |" z
4)可变参数的使用需要注意的问题
6 @+ P; J* d, q1 d
, }$ J) O  K, {9 F' E/ N" a. e1.每个函数的可变参数至多有一个.
# x( h/ i- u$ |$ R2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数., W% y7 y$ y/ s* K' v
3.可变参数的个数不确定,完全由程序约定.0 v- o0 m- S* ?( R/ l, {) |6 X
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
& p! i6 U4 k" H9 a: a7 n而printf()中不是实现了识别参数吗?那是因为函数 " ?* r5 p3 x* [3 j. N
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
4 E1 w, I( W7 W4 d5 o5 W: }6 P& |的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
7 s7 |& t$ u' F! f! @& G过在自己的程序里作判断来实现的. & {$ i  A3 L: ]# o0 L% @
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

返回列表
【捌玖网络】已经运行: