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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
* w4 H7 S7 x% P$ s1 E6 B/ o它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.! M3 \( d0 G+ y2 L. D  m7 |7 G& I; u
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
: u5 h$ B( U, E) S* G定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
5 T. X* h# m+ s$ k实际的名称与之相对应.
+ l: e$ D& C( X! Z" x由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.2 a/ u" Z! K/ O% X7 l. _9 M3 ~
然而,更多地自由,同样也加大操作上的难度.
0 R# l. t4 q. D$ s% U以下就对可变参数的几个方面作一定的介绍.
, y3 B: ?: ?1 o( j" p' p- ^% O) L) p
1)可变参数的存储形式.
- C" u0 e  k  r4 g7 [
& A2 W5 W5 w, c4 ~7 }大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,2 _4 c0 V* |3 k
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
* S) }* i( x& |: h% g在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
, Z' x& A6 J5 D! {这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.3 _# g7 b- |6 z8 u6 W
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):1 d  o+ {/ B* @- O$ Y
栈区:) L) u) ]  ]+ |5 F! D$ w
+ h0 i+ q0 m- z9 G, U# S
|栈顶             低地址: R7 H& v" G' D* J: f

, q6 h, v8 K5 E) r, q' A|第一个固定参数var1. J8 @8 c, \: F% v
|可变参数前的第一个固定参数var2# {  K  k3 x: T
|可变参数的第一个参数
7 G+ g! ]! p1 z/ n|...; L5 ]: c8 S, R' d1 r
|可变参数的最后一个参数: n- {0 v7 s4 n" D
|函数的倒数第二个固定参数var3( A& S7 K! C2 C. [$ ?  b$ ?! B
|函数的最后一个固定参数var4
$ q) R8 u7 @- C4 {: q, D|...
% r! L+ ?" ?0 q, O+ k|函数的返回地址
( I$ W+ {7 t: Q" M8 H4 w6 m' i|...7 r5 A3 B: g# U4 ~
|栈底    高地址
; r( s2 [6 P( x' i; s" s
1 Y! [, k8 J, b: f- J( t9 L2)使用可变参数所用到头文件和相关宏说明7 D% H0 _" k# k4 i+ w  r
/ l8 j, V- M5 R; H; q9 h
在此,以TC2.0编译器为参考对象来说明.
% H' E* e( r6 f, [: S" {$ e可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.7 H' K  Y. R2 Q* y3 k- B
此文件为:
6 B! e( d6 b6 U, u" p/* stdarg.h
7 R4 R9 g1 t  n" R1 Z) H* x
7 u8 f0 K# N. c# ZDefinitions for ACCESSing parameters in functions that accept
: m0 e) s4 ~  o" j( ga variable number of arguments.
$ d& ?: n4 F& N8 O1 e  L* r& m
0 C3 p! @/ v4 p# R, ECopyright (c) Borland International 1987,1988. m% U+ o, R4 S% `0 g3 W- V
All Rights Reserved.
7 s1 k1 I9 q. O( K*/
' H! M" x! i0 z; ?( ]% O#if __STDC__/ \; X6 o) P# ~( }8 z& t( ?
#define _Cdecl
2 ^: E3 @- P9 S#else
* a. Z6 k8 o/ d+ ?/ p) n2 J# ^#define _Cdecl cdecl3 i* R1 B6 ~7 p) \/ L9 t
#endif8 u0 S- H' }( D6 `- E

4 j9 Z- _2 m5 {9 H- ]. `$ A+ E" ?1 X/ q#if !defined(__STDARG)
4 ^$ Z0 m+ x' F5 S, Z3 X7 k% s#define __STDARG
9 U% i" _" v; ~) F  r2 q2 F: ~0 V3 y( g7 N
typedef void *va_list;- l% `( f2 m) K- ?4 z
" Q# I% f1 J0 B
#define va_start(ap, parmN) (ap = ...)* M, {* {; Q- a6 Z) i* C
#define va_arg(ap, type) (*((type *)(ap))++)
+ ~! o1 @2 Z" W6 B# @/ _#define va_end(ap)2 p, R8 P5 G/ x( p) [; x/ S
#define _va_ptr   (...)4 X: S  ?( N+ Z" m3 `2 X$ b
#endif% f" z9 p# w1 x  P5 y0 I- q) n

( B5 N4 E- A; v! v' J( s1 Z# f% v以上为"STDARG.H"的内容., M/ A. h. K5 {. ]
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
, i  k- {7 p5 A( A5 E# |va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
$ Q8 B& U" R5 X3 z9 A4 F+ b* o$ oparmN为可变参数的前面一个固定参数.* W! H' @/ A6 x& I( P0 X$ D
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
' g7 c! v6 c3 {) P3 S0 R1 eva_end(ap) 结束可变参数获取.
- e- x. m3 D! T' U5 X" S
6 [, e, ]8 D+ B3)可变参数的使用实例+ u/ n4 X. w  O1 {

5 u  W: U* z  J6 k实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
4 y7 D* p' X( R+ ]0 ^/ _" X
/ t9 Y8 o( E/ e0 t#include<stdio.h>
  b* h7 u- g6 w! Z! i8 w#include<conio.h>* I: [5 r% Y8 U# X- I2 S
#include<stdarg.h>
- B  N7 r$ l4 e" D5 N# w3 ?void tVarArg(int num,...);/*num为可变参数的个数*/
. k# [# f3 h( [( P) Yint main(void)# L" w8 c1 K  e; N
{
9 ], \' k3 i. ]& yclrscr();6 F& E+ W+ T( N, p" s
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");8 ~5 y" U/ {) l, X7 Q. ]
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");: F# J0 s$ H, Z: Q! z  F5 }3 h
getch();
6 Y+ ]# h4 `6 c9 b9 hreturn 0;
5 ^# i2 p' x* X4 ~1 f4 P}& i' B5 N: ^( z8 V0 b/ w
void tVarArg(int num,...); X* v) f( i! a* \2 j7 T
{
) G1 a. u! n: a" @6 vva_list argp;  /*定义一个指向可变参数的变量*/1 S, E9 n' ~* ?* e4 U2 q  v8 i
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
5 G& ^7 N2 ^: l4 l/ m" K5 i' {while(--num>=0)! k0 e8 y6 E. Q( w; h5 @, l+ D% W
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,/ N' p( V& A" m! p7 K* x& y
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
/ d3 [& Y  `$ I+ D# I1 [5 sva_end(argp);  /*结束可变参数获取*/
: O/ v$ W* ^  L4 G6 y0 x  Nreturn ;
  y  Q) s' o* `# ~) x: T$ f+ Y}
+ y8 e4 m- F- T  B4 i: T
& t' z; B- y! H/ I7 P: W, u) c+ {4)可变参数的使用需要注意的问题
( Z" W& G; K5 u; J& B$ U9 `! ^2 l& v3 i) T2 ~
1.每个函数的可变参数至多有一个.1 N( u. p+ |; u* F: }  y* q
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.: i5 e- x/ v$ s  j% r8 ^3 b/ f
3.可变参数的个数不确定,完全由程序约定.
! T4 O0 Q% G4 K5 _; _2 F4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
2 m& V  t! v! f- d而printf()中不是实现了识别参数吗?那是因为函数
/ w9 h% u$ _  Jprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
! j! q3 L8 \% s的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 & ]4 v) J+ S% C9 O
过在自己的程序里作判断来实现的.
& R5 e! ^4 Y# @# G5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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