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

函数的可变参数详谈

可变参数的英文表示为:variable argument.9 x; c. Q3 y9 Y
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
1 [7 ~* x% r& e3 S& b可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不: \2 L7 p- D2 Q2 h/ n% R' a
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有/ ~$ B$ m$ Z: Q8 O, [
实际的名称与之相对应., p+ W6 A$ A  P2 a
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
& b. X) a2 X& y  ~然而,更多地自由,同样也加大操作上的难度.
3 B2 n+ h1 I4 z- h9 l& F  d; M以下就对可变参数的几个方面作一定的介绍.8 t! y! n4 ~* N/ M1 L* z2 b
2 w  {4 Q% U3 Q7 v4 S  J6 h
1)可变参数的存储形式.
) s! J2 X$ S( Q: m7 u! P# v3 U# J1 B$ V4 H
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
, q- l) y. \& @" L存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
7 I+ S* s0 Q. W在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
8 n4 Q3 v6 B- Y! V1 d0 U# E这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.( I. h7 ^. Q: M8 f
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
$ D) y2 R' b  f  [' O* V: y栈区:
; M. F" ~1 O) c: K3 |
% s( v* c$ P, m. ], A" Y|栈顶             低地址
4 l7 u% B/ t8 i$ b, W
2 \( g6 S& H' d1 A1 s- |! ?$ s|第一个固定参数var18 l( H# `3 c* e6 ]0 P5 h
|可变参数前的第一个固定参数var21 F( e( R6 }( k/ H% J. Q
|可变参数的第一个参数
$ c' h1 I! R, Q4 D3 ~4 X|...8 P: d$ {. K' s: c  z0 U
|可变参数的最后一个参数$ s" l5 ?/ ]* [
|函数的倒数第二个固定参数var3' z) b4 D# O+ w
|函数的最后一个固定参数var4
# J! J5 q0 u9 ~* s- E. k|...
2 O5 i) \) [5 P' y|函数的返回地址
$ Z9 W4 O( u1 E9 t% C|...8 e; t# H0 D0 O- Q* G7 e. F
|栈底    高地址8 B- j2 R0 y* G% A
* W! i: S7 H; {" W7 j
2)使用可变参数所用到头文件和相关宏说明
- S: Y5 b" A7 o9 [/ T% F7 v4 `9 y8 s+ e6 s1 M( M/ Q9 P; f; b
在此,以TC2.0编译器为参考对象来说明.
- e# Y% }, v9 u0 t" v& Q: t( \可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
; d! g# a) w" |4 b* S* }6 X: h此文件为:" B/ H% ]+ k( F6 R& R; c( v4 E
/* stdarg.h
- d) _. H1 W. A3 P) p+ S3 X
$ f0 k1 ]' x$ mDefinitions for ACCESSing parameters in functions that accept
$ l: g8 H7 R9 j% \. Ga variable number of arguments.8 H& m6 t& n, h! m% B. ^

3 y* |/ ?5 g5 @0 w0 C0 Q- CCopyright (c) Borland International 1987,1988
! Y7 G% y7 W3 v  sAll Rights Reserved.
: n3 {/ C, y0 i$ J$ K# J*/
4 I% I" ~$ t6 I#if __STDC__+ k6 C: ]2 U  J$ k- u# ?
#define _Cdecl8 D" r7 I' k' _8 z' g* t' H
#else
; P& p, Q( w& C4 H9 Z* B#define _Cdecl cdecl
. O6 c8 v. b) _6 ~/ N. v#endif. N) E/ K, ^& E8 k: K4 ^

& |3 I! d7 x# s% S! a; O6 [#if !defined(__STDARG)6 W7 s8 H" E$ ~- P# N" x* ]: b
#define __STDARG6 Z8 k! d+ `, q/ B  y* ]; g

) @# T, \4 O& o* X" Z1 a" Ptypedef void *va_list;9 q3 ~. v0 {8 h) w1 U" w" s. i

# o$ w3 E+ R: W+ L2 G3 c#define va_start(ap, parmN) (ap = ...)& X: _1 f* T; L+ B9 U7 K8 i
#define va_arg(ap, type) (*((type *)(ap))++)4 t( {' W, d. g3 A; R
#define va_end(ap)
' l: [3 C) r8 u#define _va_ptr   (...)
+ z/ ?' w) [6 C% f#endif3 ?2 g1 q$ f' D) v+ R1 y9 [! P
4 H, V2 F& c) h0 `2 |9 m& o, e
以上为"STDARG.H"的内容.
  ]* x+ K0 d. |* s  Q7 r3 n' ~该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
2 x2 u- X; T$ g" dva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
& R- X4 T" s- iparmN为可变参数的前面一个固定参数.
  S/ b) _' [+ q# K# M/ t! t# Qva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.3 V2 ^7 d1 V; j3 Q! ^/ l* n
va_end(ap) 结束可变参数获取.3 W1 q8 C/ |( w2 S

0 O6 c. h+ i) L( D' _9 U+ D3)可变参数的使用实例: Q+ G' M: v5 J1 X% N; H
8 |4 c% }, f! l" M" r
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.1 t7 Y3 M# i# I  a* a$ s
! ?2 s% f% a) B
#include<stdio.h>/ f8 N$ w, e( j. m: P* O; h
#include<conio.h>
& v, N. Q% [  l* \+ \#include<stdarg.h>
  d7 z3 j0 ]7 y/ cvoid tVarArg(int num,...);/*num为可变参数的个数*/
+ P# Q0 t4 u* w; z7 Cint main(void)
' C0 I7 G$ V' O, ~{
3 h! U$ X+ R  C/ v+ n' mclrscr();
9 P, r0 m" [* d  a* _tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");1 W7 B2 N; W) l% ]: M$ `
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
6 Q% u% Y- z* j0 u/ S- Ngetch();0 ~3 Z/ w6 _& Y5 c6 n0 T
return 0;  E$ o3 j8 F1 X; I5 o+ {
}
  K7 A) l, ~; C2 d$ Q! `  V( |! D' S% Dvoid tVarArg(int num,...)
, e/ @& J. Z/ _% o; H5 w{
; Q: ?9 V! n/ z; B* Hva_list argp;  /*定义一个指向可变参数的变量*/8 H( ?. L( e1 D, N* u3 c
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
$ ^/ \+ [- }# m  B. L' [while(--num>=0)1 C% u/ K, [4 Y% M- ?
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
: f# R+ e# F$ ^8 ^    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
! s' Z: P6 r4 q: A) X' ]4 xva_end(argp);  /*结束可变参数获取*/) X" G7 G" t1 G7 @) l! Y: K
return ;
7 s/ L. k9 _. `0 P}$ u/ R* W8 c/ k, ^6 w
& e( {, \; ]% f# L9 S& N
4)可变参数的使用需要注意的问题
* J  ^9 o/ [$ F* ~. }4 `7 ^5 L$ n; @: l; A" _
1.每个函数的可变参数至多有一个.
0 a0 X$ |0 S7 f2 {9 d8 J2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.5 }/ _8 k- O) `% r3 c. v6 i) I
3.可变参数的个数不确定,完全由程序约定.0 g0 O0 B- F& G+ b1 }
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换., U7 F  r) K$ @. G5 F6 B" h8 b
而printf()中不是实现了识别参数吗?那是因为函数
; e! L! S  F( F& D3 y- c, Lprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
5 _* M  c& K$ V! x6 Q+ _8 J/ F的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 3 [) ?$ ^3 T% i# d# X4 X7 X
过在自己的程序里作判断来实现的. 6 a3 n9 x0 G% f+ K. i
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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