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

函数的可变参数详谈

可变参数的英文表示为:variable argument.5 ?" K: f! \+ J5 M/ Z- P
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔., z$ D$ r9 ~% u* _* K1 S! s
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不+ k1 `5 ?8 c  z0 v: X4 w: j3 o
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
/ Z& C5 J8 {5 f& X实际的名称与之相对应.8 {6 _0 V( z# R3 _7 H  b
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.' t* C  b' h3 g& ~5 z+ l8 u) M
然而,更多地自由,同样也加大操作上的难度.
3 ?; r! ]" G# ~" f( E$ `以下就对可变参数的几个方面作一定的介绍.
  Q2 F% {. c$ E9 J3 w' l4 }; u# E0 X* Q6 C; f* c* u: K- w' o5 A4 ]
1)可变参数的存储形式.6 X; a3 [3 g1 f' t  W

+ {3 T/ x: Q0 E# j, Y大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
3 o2 x6 y: G+ |7 N存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.8 U1 ^; m4 H, [/ g' Y  W: ?& M" q
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,  I  j' {3 }- w/ r1 J# L
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.+ [* D* k9 B  A. `8 C
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
0 Y1 G. _) D4 R5 l5 L4 v3 \栈区:
+ l; `: b6 r/ T2 n
, W% i  G% A3 {" f|栈顶             低地址9 r; Y+ K% S  J1 r
7 U& j# o8 S+ E( N
|第一个固定参数var1
  i- F0 D$ S9 S  s  s' F|可变参数前的第一个固定参数var2; H% T$ _0 b# g2 v% q
|可变参数的第一个参数; s% ]$ f. k5 S9 `
|.... Q" o0 F9 P7 }; a
|可变参数的最后一个参数  |8 b, p% @- v; i- H, J
|函数的倒数第二个固定参数var3
: ?+ F1 v. P/ L, x4 B4 O|函数的最后一个固定参数var4; @1 V; J0 E/ I; I6 x
|...
7 Y$ f& F4 C. G- T/ q. C: c) `|函数的返回地址( {* D  t9 X! X$ d4 t
|...
+ x( q5 S' a4 I6 K8 Z|栈底    高地址) N. X2 h, N* f: m. F" d

& i5 i8 b# {5 I3 B( s2)使用可变参数所用到头文件和相关宏说明. W: o4 ]. u& ]' b/ t; J9 l
7 d- X8 D5 n' \5 e4 _
在此,以TC2.0编译器为参考对象来说明.. C9 q& [" @: v3 L' r; Z. r) R( V
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.! {0 ]3 C3 [: h" P) w
此文件为:/ |1 ]$ h# Q* O! q/ r3 T, O) r; R
/* stdarg.h
( a$ z' H( L& a# e" T  J  a* ?" \; e! p. `5 f* M
Definitions for ACCESSing parameters in functions that accept! J1 o7 ?6 B9 v2 A1 f; J/ u
a variable number of arguments.
& w8 \  E( `& l: ]0 n- D) O
6 ]; E/ A* C+ ?( m: a" ^Copyright (c) Borland International 1987,1988# F4 k' i8 {7 r( w
All Rights Reserved.( @8 ?& M" x: I
*/* s! w8 L! o/ c7 o& U
#if __STDC__
4 h6 V) C: K+ j% x6 v; c#define _Cdecl/ E! Y/ H& Z7 e4 F  ^
#else
5 S) c" x7 e( g# }#define _Cdecl cdecl
- M$ m' i5 K$ B9 R) z* }#endif. {  Z. s( Y8 W& }+ K4 K0 ?- U
) Y# c8 h2 x1 I1 ~' y) |
#if !defined(__STDARG)5 W2 C" i! s9 U/ Y9 y% Y4 V
#define __STDARG
, U& ^* H3 _4 n1 H3 R4 w6 A
8 D+ u" M, U8 Q- b# m4 ]typedef void *va_list;
" x- b; I, |8 w
; g# ~% V+ w$ ^* x' I) g#define va_start(ap, parmN) (ap = ...)
, ]% h/ u4 q. E* C  k  p" [5 p#define va_arg(ap, type) (*((type *)(ap))++)
9 o7 Y% d, r0 E% W. m2 g$ E9 o3 }#define va_end(ap), O2 C, d8 z+ z  d6 h) w% S4 ]
#define _va_ptr   (...)
" @7 }. l4 h- i#endif
' s( `4 [1 k+ F8 u3 ^) T( W. F  K% [7 Q4 }# h
以上为"STDARG.H"的内容.) M. \0 j& f& {+ w
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
0 X  r( [7 C' G" \) P7 f/ Ava_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,: m  z& U0 \. s/ t$ c
parmN为可变参数的前面一个固定参数.  M! O9 l2 {; t; R; j2 f( A6 _. ^
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.) e% h$ P5 R6 I
va_end(ap) 结束可变参数获取.
; k5 J; Q* w" X7 m5 M5 |) g$ X  b0 [6 ?
3)可变参数的使用实例& p8 K9 R; X# B! k4 O

  }0 V% B2 J  T% ?7 H9 N实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
2 ^  A2 W( j4 O* V9 Q3 {% h; R+ v- {% D% N% e" ]
#include<stdio.h>! ?  y$ h4 B. B$ e
#include<conio.h>
) g7 D8 A# i, x+ o+ g  k#include<stdarg.h>
) A. z1 y) g0 f3 B$ Svoid tVarArg(int num,...);/*num为可变参数的个数*/5 X  q: T# x+ J# J: r. ^7 Z% I
int main(void)
3 a' K1 Y! k* Q  k5 u{# e; }5 h4 o0 r
clrscr();. m: |2 y% q& w$ W4 A* j, i
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");1 d5 T) c  {6 w+ c- T. B+ l# Z- m
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");' @* ], `' h1 V4 m# d
getch();
5 T7 `( |1 h$ K0 mreturn 0;
7 [7 t  b5 _0 {6 Y}9 y9 b% f& c# w# A8 F6 x9 d
void tVarArg(int num,...)
: G, [9 D3 e. E{
3 g. u3 a1 E8 {0 b* v2 pva_list argp;  /*定义一个指向可变参数的变量*/  g' [& w/ j$ \9 m2 a
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/. v' M, P$ ]! p
while(--num>=0)
) x! D2 V" q5 G' Y. k7 N7 W  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
3 `1 A6 o& E& i0 |6 x, K: i7 K+ C4 n    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/0 c! U% P: R! i* D
va_end(argp);  /*结束可变参数获取*/4 G8 e' t* m3 u7 y0 r
return ;: V: L1 _$ |" B! E
}
, w% f% z* s2 I6 f3 F5 R% C' }+ n' N( f6 @( l9 @" N$ n9 H0 O- h
4)可变参数的使用需要注意的问题
1 n2 [9 ^+ ~" L& u8 C* c8 }# `8 z# \7 n: l. C( t" x$ Z; R6 t5 S9 O* P
1.每个函数的可变参数至多有一个.
. K7 Y+ @& z/ n7 m) O$ ^2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.' h! g! C8 O, f" A! \7 X0 D- U
3.可变参数的个数不确定,完全由程序约定.! c3 T7 @+ [! J
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
1 I% l+ {8 D( ]  S$ i而printf()中不是实现了识别参数吗?那是因为函数
, x6 z4 {% |" E" Aprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
2 T2 U* G# n- [  L6 D9 l. P的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
4 }- h  W/ |( X$ b* b# v  B过在自己的程序里作判断来实现的.
' `# Q0 E& p& U- T7 \5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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