返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.
# S5 K, W* B- C- @% }# H! A它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.. w: p! [' o- J. }& }( e+ p
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
2 J; B; t, b/ [$ @定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有; s% _9 ]) Z8 R; f! s
实际的名称与之相对应.
- E& D# x, H3 K4 t% @: u# [, D由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.& O0 p& D1 G# n) x# N
然而,更多地自由,同样也加大操作上的难度.. u6 P( E( p( r3 T0 C% ~: X
以下就对可变参数的几个方面作一定的介绍.8 x0 P( N0 F5 J* l% ]+ H& Z
- y5 b) j+ I( P# F3 S
1)可变参数的存储形式.4 H/ N, \8 @7 s/ V
+ i: L/ g8 q4 {: v) y
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
" b7 g2 w2 Y$ K9 i% k( y存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区./ x! ?! ?) Q2 R# R6 K
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
  Q+ D# T, U" ]( @这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.6 m$ S7 i( E$ a0 ?1 {
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):" ?+ j. B9 u: v5 F8 U* X" ^" Y
栈区:
# ?6 }% E( R# w) H2 ]3 B, g5 C/ y* D% n; F
|栈顶             低地址
1 A: _! |/ I/ {1 V+ h
& X0 @0 [( ]4 ^( s1 ?8 ]$ x|第一个固定参数var16 H. D! G6 L+ v8 y) `4 e
|可变参数前的第一个固定参数var21 s6 u0 `' U1 h4 n
|可变参数的第一个参数: _3 }" I! J# c  h' W
|...
( F& Z+ I6 L; K+ @$ x5 N' k|可变参数的最后一个参数
& ~$ A& Y& p) N6 c. P|函数的倒数第二个固定参数var3
' m- V6 \% e( A4 v2 X- L" \|函数的最后一个固定参数var4
  l1 e2 M$ q) W# `" \|...
1 T( w5 ]" k5 d: i  w  {# ]. b: v|函数的返回地址4 j6 d  ^' C( d/ I
|...
  N* F6 J' `* w- V2 P|栈底    高地址
% o# m% U1 \6 a/ O8 \$ Y! P
) r+ J+ @8 {! g) E1 U8 I: _2)使用可变参数所用到头文件和相关宏说明
  i! C" ~9 S5 T
! [) _5 y- u9 ~- l在此,以TC2.0编译器为参考对象来说明.9 E4 o, F$ I- E6 Y4 E
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.  z2 ?( a. z& T: W4 d8 o" A
此文件为:
7 X, g0 s9 f3 c6 T/* stdarg.h
$ C, K( ~, z9 Y7 z) z2 ^; U# U( S" F$ Y! V
Definitions for ACCESSing parameters in functions that accept' S0 ~" S- p6 ?: t4 W
a variable number of arguments.
7 L5 |$ T  p: e; g8 z0 J; n7 l- r1 {9 ?: w
Copyright (c) Borland International 1987,1988
" h0 k3 E) E! P( z( B- ~All Rights Reserved.: c2 d% @& |$ r: S+ r/ G; Y- S
*/. o* d& \1 `$ z( u% w4 K3 x
#if __STDC__
2 Z) Q# N7 H. C8 T#define _Cdecl
% a0 ]" R  R: v#else
' S1 C1 G+ ~  h& H# V0 ]' f#define _Cdecl cdecl8 k0 l" j" k6 j) S4 l+ l
#endif
" \) b; X% f( i. {. O8 Z* o' K* ]8 d
% E8 T5 R4 X: X#if !defined(__STDARG)2 v2 ^/ j: _$ d+ p! s
#define __STDARG
  @; h7 w/ ~# N0 M; K# P$ c$ m( D, E
0 S, b+ R( T4 w' H0 Q: }+ ^typedef void *va_list;
) d* o$ H& }6 h9 ~4 V
3 Y! f7 e  |: H#define va_start(ap, parmN) (ap = ...)9 P+ R4 h2 L% _4 n% ^
#define va_arg(ap, type) (*((type *)(ap))++)' s% a: ~' S" K- d1 n) Q8 G
#define va_end(ap)
% g# `+ Q7 o9 H1 w) s8 K#define _va_ptr   (...)  ~% X' e: B  ^/ z. ?* S
#endif2 ^5 f: m4 o3 T9 y4 t# h4 e, [1 e6 l

/ R' F% V5 ~7 [; n( [" g: ]以上为"STDARG.H"的内容.
) o7 D: F6 e% k$ q) F# h) h该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
- h" n0 G4 q# G+ Q0 C3 o0 Z8 pva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,& k4 n  W# c" j# s4 Y
parmN为可变参数的前面一个固定参数./ Y+ H& A: `$ r$ V; }
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.+ Q* _/ a) c8 y8 t
va_end(ap) 结束可变参数获取.) f( y* m: h8 u& r/ k# K* d6 [

# r$ o( ?% `* C+ z5 X( _) ]3)可变参数的使用实例4 N2 o# N7 r! S0 K0 K
( t# B+ ~7 X0 d: O8 V2 n
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
7 B9 a- c7 q9 I; N9 o# b
/ f& `: \; {7 G. V#include<stdio.h>
3 U7 m0 R, u  _: W- K6 [: r1 g#include<conio.h>
6 B5 ]: Q% T2 P/ G#include<stdarg.h>
8 x- e! q- o3 vvoid tVarArg(int num,...);/*num为可变参数的个数*/
& \0 H( u- h6 W: m- J1 G, @int main(void)) x* Q- q7 C) F3 r  Q
{1 m. P  `& q0 p) z; f
clrscr();0 R+ B' k' O6 c5 w$ G' {5 B
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");1 ^3 w& E" t+ p2 V
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
. x. A1 n' E+ j; V$ w8 ngetch();
/ r# {: R! _3 S2 i6 v# h$ sreturn 0;6 U6 H& k( @3 p7 J& Z
}
. e! M* {% ?2 B6 T6 W' X* ?: ]void tVarArg(int num,...)
6 `4 t5 L5 p1 p0 g9 e{3 y8 F/ M1 w5 C
va_list argp;  /*定义一个指向可变参数的变量*/- M" J6 ^& u4 `! M' g
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
: a+ q1 X' W3 bwhile(--num>=0)
& J9 W7 L. W" a1 L- m" m5 |9 ?7 n  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
5 d/ I3 |* W, i5 Q, e; U8 p; C    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
8 m/ f1 u0 x$ I+ B9 a3 [va_end(argp);  /*结束可变参数获取*/5 m# Q7 m( g' S4 g- K
return ;' B9 `6 x0 b7 i1 R4 C8 K) E$ s9 g3 h
}
5 ]* W/ @1 h7 \8 }, h
5 p6 L; y; ]! H: B1 R4)可变参数的使用需要注意的问题5 G3 C* b- R  C' J# ]
( ^) e# q, {' j9 L
1.每个函数的可变参数至多有一个.: Y) A, U. R8 Y7 j) h) }
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
6 p" w6 q5 J! A1 U" _- r+ X- o+ w3.可变参数的个数不确定,完全由程序约定.
# N! ^" ~2 v, t4 p+ ?" Z. x4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
. Z- {: g( k/ u& p而printf()中不是实现了识别参数吗?那是因为函数 9 M- G7 m: B: q+ J# e. d6 O1 b
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg " [! O4 o! u# l
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
6 p0 Y# k# p- m( Y9 J9 e0 v2 D过在自己的程序里作判断来实现的. 0 [* E) w/ \: P. Z$ E6 y. X* }' l
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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