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

函数的可变参数详谈

可变参数的英文表示为:variable argument.) e3 r3 V! l4 U; d7 U
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
3 H2 O, ~8 t. @1 X) T) ]- F可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
. H7 u8 T% E8 I. a0 i9 B- z定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
8 q; q/ k. t1 {" l+ I实际的名称与之相对应.
5 g! T2 h) J# [# ]& g+ V+ x+ i由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
, S; H: l" p$ [- U' y然而,更多地自由,同样也加大操作上的难度.
3 a* g9 \4 H1 ^以下就对可变参数的几个方面作一定的介绍.
. D: f& Q/ x, S5 K. k) w; h( z" F
1)可变参数的存储形式.! o4 g* T* C3 Z, l8 ^( t, f. ~
- d3 n. ^/ ^4 F
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
, h% X2 F/ A# D0 a$ o8 D, }存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区." T# {1 t; a/ x# l
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,7 y% u) m6 D1 K, n# M
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
% u4 g6 Z  s: l- p因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):$ h& G0 c4 V1 u  k1 P
栈区:
# z5 T$ u6 A+ m& `0 r! t$ Z6 ]: v" x. R
|栈顶             低地址/ U1 l+ A! O7 P3 F" _& V! V; Z
; \: D) C6 H; l! t; G
|第一个固定参数var1/ G, ?1 \. O$ i9 s3 t* C) U5 Z
|可变参数前的第一个固定参数var2
! a$ X7 T7 Z4 C6 S|可变参数的第一个参数  @9 F! E2 v" \( ?% t
|...
4 Z: j% Q9 h; ^) v5 l|可变参数的最后一个参数
2 q/ n) l' i' S/ _|函数的倒数第二个固定参数var3
0 r7 V4 ^% ?: b, q6 s* C|函数的最后一个固定参数var4
4 m4 _6 y6 ^4 g|...- H8 F5 ]2 m4 m# P3 l
|函数的返回地址
! E" F$ J& z5 S! O) I  L* |4 _|...
/ Z# T' X8 D! _4 {|栈底    高地址
1 e. e) e- t8 X% o6 m7 ~% f
) Y" `* e& \6 U! J2)使用可变参数所用到头文件和相关宏说明" S7 y6 |  r  u8 s. b

* K4 M! Z; ^/ J& m1 ^( q在此,以TC2.0编译器为参考对象来说明.
5 ?/ C: f4 j* e9 N! c可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
8 o/ h0 [9 b% C; w2 X4 y  z, X此文件为:
5 b! V6 ~! q3 x  t/* stdarg.h
4 j! H- f/ J# r( C. j6 @9 E1 l
6 Y  L+ w6 E; u4 v: {& |2 P" `Definitions for ACCESSing parameters in functions that accept
6 ?' N4 g0 u$ o$ |" ya variable number of arguments.4 B! S% Q/ `3 S( b1 b) |  \  K" m

' v* x8 J0 h5 w! q9 ~+ xCopyright (c) Borland International 1987,1988
4 K; `; K1 ?/ sAll Rights Reserved./ u" ]3 |" V# S1 c- [
*/
5 S0 s2 C0 x9 L7 A, [#if __STDC__  W; j7 P; {* F# p4 ~+ F& X
#define _Cdecl
3 p/ x- |4 l( d" j$ G4 |#else
/ X# ~( T. C* u1 M0 ^#define _Cdecl cdecl* ]5 t; B6 n( d  d
#endif
0 l$ a5 r3 w1 Z$ V' Z# x: G4 v+ }+ s  O9 l
#if !defined(__STDARG)
8 y9 |  H3 T# D% U#define __STDARG
0 k3 i0 A$ g9 p
$ _: u- W* O2 N3 t/ ktypedef void *va_list;
# G/ v! V3 _4 n+ E* k7 [7 |0 j, q3 B! F) A. j$ ^" J2 V" K6 t
#define va_start(ap, parmN) (ap = ...)
7 y& l% E5 N" z/ ^#define va_arg(ap, type) (*((type *)(ap))++)
' N" r/ q, D4 A1 e#define va_end(ap)" Z" R5 A5 @% O# y
#define _va_ptr   (...)$ h( s; I( n" ?$ D9 `# i
#endif! \& O: h0 s. L# C& V! k# V
" B2 v6 n# r9 W( N. c7 p
以上为"STDARG.H"的内容.
: O' c( i; r$ r1 i3 b该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;1 L9 R+ o% |! Q- `3 h% {5 \
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
, K/ Q9 k% s, X. X0 |parmN为可变参数的前面一个固定参数.
. E; u9 ~' s# J; ?8 {# ]6 \va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.- y8 o2 H  A* }% r, K
va_end(ap) 结束可变参数获取.6 @6 [" H' q' c) k/ ]( J

& r% k" h  M* X  {: {  {4 D; k3)可变参数的使用实例0 ]% Z5 J$ r8 ~$ b5 Y1 B
, y- S  f5 E& S7 X: x- |) y6 g
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
2 B0 Y6 D" I8 e: m
. s" p6 [* q  w: F#include<stdio.h>
8 I. R/ O, S& X6 k#include<conio.h>
+ g8 }8 n' [8 u9 R3 i1 ]# i3 D#include<stdarg.h>  F. o! Q) d7 i8 L- m6 m5 A
void tVarArg(int num,...);/*num为可变参数的个数*/
! @: }& x+ s7 {$ Q3 ]int main(void)% {% o- n# ^, M3 p' k' `. i
{( X! {9 g; Z) s$ r
clrscr();" l' t- I$ U6 n4 [3 }  U
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
# T" I) B7 z6 _4 _; ?7 r- U8 z8 ctVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
' J7 y) M  d, c' i! R+ tgetch();8 `% d# s: \/ p4 M5 ]3 V
return 0;4 g/ o" U$ g' Y# ~5 F/ h+ b# r$ q
}
# v9 n8 O/ s  a; P! r' ]  nvoid tVarArg(int num,...)
) a) r$ v3 g1 P: I, U{
9 s5 v# C( h  G% T0 H' nva_list argp;  /*定义一个指向可变参数的变量*/7 ?. d# u: y- c# I
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
& @# g" w$ T7 s; Lwhile(--num>=0)% o5 O' B0 @% f$ T
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
" h3 [1 v" _& n9 I+ X    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
' _- P  m" ^5 n; Xva_end(argp);  /*结束可变参数获取*/- u% e3 M0 C' p! N6 i: o, a7 l( `9 {
return ;
8 c/ s' ^7 K; x+ t  E9 i}
+ A8 v) {; E# e( Z% [9 h/ w2 H% J. o+ n0 ^5 J
4)可变参数的使用需要注意的问题
) D/ m# n4 }( O& r0 s
/ \2 m: P0 G& A* p3 R1.每个函数的可变参数至多有一个.
6 `. O& f5 q. @6 g3 O2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
4 V4 ~8 i- }4 I3.可变参数的个数不确定,完全由程序约定.3 z; q9 y, d4 H
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
% m" L# r6 K3 _9 Q而printf()中不是实现了识别参数吗?那是因为函数
! P( S1 M+ U, Q: _7 f) eprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg , X4 t* i8 `! e, T$ w" ^
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 $ K% Z+ D( Y2 f/ }! B9 n. @) I
过在自己的程序里作判断来实现的. 8 B+ h9 V) B! l6 t9 f, G4 M
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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