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

函数的可变参数详谈

可变参数的英文表示为:variable argument.1 E& y! L7 u* v( I' c% `0 G
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
& y% v' W" V2 t4 R可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
+ W' I: P# L! s3 \: i* G定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
& G  N& z& V6 O. |: U4 }实际的名称与之相对应.9 s$ d4 N& F# d+ f
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
, Z) z! e' [) I, `6 z9 K然而,更多地自由,同样也加大操作上的难度.6 p) [3 ^0 N- W- s
以下就对可变参数的几个方面作一定的介绍.% G* m: u2 z2 P4 j6 A( i
& V1 R2 ?6 |" w8 w7 }
1)可变参数的存储形式.
0 {, {- h. v, ~6 i+ |
& J' h1 H3 ~# q% s+ v$ M大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
: j1 J; ?1 q7 J1 O3 M) T存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.& r9 b8 }; `- w1 t( T7 p( @* {0 [
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,) K: [& ~2 _  C% r2 n4 Y
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.9 _5 f+ k2 |8 R
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
/ d$ O) {0 ?5 T6 ~# @' _栈区:
8 x* n5 M4 @6 D; d, @7 P' c; a0 r: Z- ]6 W  L+ A) t% o
|栈顶             低地址% G$ i6 W' o$ r1 v8 Y

% _* b/ S5 n; N4 |6 N/ _; r. P* L8 u|第一个固定参数var1$ U: x" O* z* w. q$ x5 B# r/ N
|可变参数前的第一个固定参数var2# j( _' _( \, n" M( q: ]  O* e
|可变参数的第一个参数/ a; r3 d7 d9 R0 }8 V. O- z+ t
|...& f- X( u: T; X$ F/ F5 r4 x
|可变参数的最后一个参数
  E3 w9 Y1 ~# K% k, c! @1 r|函数的倒数第二个固定参数var32 m5 e* R/ r, E3 `9 E& Z0 j7 _4 F
|函数的最后一个固定参数var4; {* W+ _& U# Q( r% T  @2 ~
|...
0 S6 i, A7 D! x3 i6 r( t; q. n& k% F0 {|函数的返回地址' G2 G6 V- e5 A# ?+ Q5 T
|...# i$ J; u  ^6 Z  V7 D, {) i
|栈底    高地址* u+ b3 A  e0 m
& Q. Q) N5 R" x5 @
2)使用可变参数所用到头文件和相关宏说明3 [( y" ^& j2 D

$ o! l- X7 h1 `3 `( W在此,以TC2.0编译器为参考对象来说明.- A' t; W) O9 l2 Q: |0 n6 y7 B
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
0 R. _. r" R, l0 D' }此文件为:
- Y( ?: i7 n: t0 N/* stdarg.h; F# Q3 _& U" G" u$ A

: i! A( @: B. l& m$ Y- A7 bDefinitions for ACCESSing parameters in functions that accept
0 w+ q; t* O$ f; f# {; U- ?a variable number of arguments.
1 @$ n. B7 D- I3 V, Z% \! ]; h
5 }, l' |. ]: N. I; l- J+ b6 Z' BCopyright (c) Borland International 1987,1988
# q1 ?6 I+ `; N" P2 h( `) `$ L- NAll Rights Reserved.
5 t* ?. Q- Q2 i$ w4 C4 ?/ @*/
+ C/ \) g. ], I6 |( S+ }#if __STDC__; j! ~7 N) e$ `4 ^
#define _Cdecl
2 ]4 q8 Y; R, R5 l3 ]#else; Z% R1 u0 u+ G
#define _Cdecl cdecl0 {3 K& |0 G# f, W* n
#endif" B: O5 n$ n: B/ x. k  M

. {+ @2 f' |& f, Y1 N4 d#if !defined(__STDARG)' \0 t$ |( `) l
#define __STDARG
) U1 [4 u+ ]2 j! [. ~! P% n5 w, z7 ~' I! s  E
typedef void *va_list;7 Y2 G1 @- i# O. ~; v& b; J3 m
2 V& b6 G& c# p$ O% b" V3 Y. ]
#define va_start(ap, parmN) (ap = ...)
) q' ]9 W) r& Z( y  Q# }# {#define va_arg(ap, type) (*((type *)(ap))++): `0 d9 ^6 y% D( d! Q7 q4 ?
#define va_end(ap)* k7 A6 O+ K  f) v& T1 w7 O3 o
#define _va_ptr   (...)! x: [: g* F4 t( @- |7 h4 L0 U5 i
#endif
: X9 q2 l5 e! O4 E" G" ~. ^% E- w0 [
以上为"STDARG.H"的内容.2 x; X. P. o3 h7 N
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
' h" |+ \" E  s; Hva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
5 }/ C& T6 A$ j- T& jparmN为可变参数的前面一个固定参数.
: `' q% U7 O: o0 x7 h2 sva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.6 y$ Z4 |5 H3 O; y& T* w& l
va_end(ap) 结束可变参数获取.
5 t' t5 a2 P5 Y! p2 r
: N- o  I5 b" [& d3)可变参数的使用实例
/ G9 B5 T- x% H* Z2 k) c  `+ r+ z6 X4 |
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.* w$ D3 c- V$ X2 ?0 v

- q+ W1 J2 R( l2 ^0 N#include<stdio.h>1 i* b4 G" O, x/ T( T" ^3 m8 E; v: A
#include<conio.h>
" Y. }3 B' c  F#include<stdarg.h>/ N: _( Q" d4 M
void tVarArg(int num,...);/*num为可变参数的个数*/
5 Q/ G% U6 W& @7 sint main(void), R8 }2 @) \6 u6 f
{3 ?1 X3 K0 n) Z5 q+ U9 ?; t7 C
clrscr();6 v) }& Z9 {' ~4 ]+ z
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");+ {' b) P, r- S* D& x) ?
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
8 Z8 ?* L$ r" n7 `7 H! R4 ugetch();
! O! D$ t/ k. c7 a* D# Oreturn 0;7 t  O, f# R3 X+ i  D$ I# M( Y
}
$ v) ~% \& x4 E5 Gvoid tVarArg(int num,...)
0 V2 T* {/ n' f& D9 i9 S{- `( I6 i  Z$ v0 Y1 W
va_list argp;  /*定义一个指向可变参数的变量*/# {3 S3 Q5 H( v( Z
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
. @. l! g1 y% c& Q0 dwhile(--num>=0)
! x6 C, W/ x7 y  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,! T, k; a  J4 a, l
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/+ G; p7 h; S/ S: S& ]
va_end(argp);  /*结束可变参数获取*/
  i/ @# I& Y1 c( W2 }' J! Hreturn ;8 r3 K1 l  h$ \& b9 W
}4 d# w4 v" ]; v

& _; {- \1 N; H- o3 e! U4)可变参数的使用需要注意的问题5 j9 X# N) T( h9 h/ k! i

3 w0 y) S9 n7 e( C# K1 ~$ F1.每个函数的可变参数至多有一个.
4 P" H7 v$ ]$ c6 T! b; G1 ~2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
+ U3 t9 x# y; D8 j3.可变参数的个数不确定,完全由程序约定.5 p2 b* }. Z1 v6 |* k
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.% O( P( e- u+ F2 E+ v/ a
而printf()中不是实现了识别参数吗?那是因为函数 8 i& T' f8 V* _5 l) B
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg # i/ U( W  j  t: I2 L; z
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
: f" D! f6 B! i6 Z9 c过在自己的程序里作判断来实现的.
$ |/ Y- J  p6 \+ a$ @5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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