返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.. d3 R( c+ t8 d' j: V2 d  [
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
" y; X/ O( i  V6 P可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不9 `3 D  D* I- S! x
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有+ A. V% b2 T/ k" m* c( B
实际的名称与之相对应.4 C2 P5 R! a0 {( |4 C5 Y1 @1 P
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
- B6 M, x. H+ ~( p$ f7 N3 F然而,更多地自由,同样也加大操作上的难度.6 q; |! H! P" Y
以下就对可变参数的几个方面作一定的介绍.
1 h6 [' n5 V0 Q, w
2 _2 w4 N$ K% N- a1)可变参数的存储形式.
1 B  i5 H; C, U. {& q: z% h0 ?' X" W! X3 b2 f; P1 x+ |# C. Q/ j# F6 J
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,9 g) J4 i' z2 _4 G6 l* r( E& S
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
5 r: d) q2 E* T3 h2 c  C0 Y9 k在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
( V3 |# ?) T! W5 l这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.' {* i7 K/ l9 C% ]5 @' {
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):& E- b. [4 ^) W
栈区:
/ q& c" f- S+ e0 p1 w6 P4 y6 T, X2 A2 z* C
|栈顶             低地址
( D$ M9 e2 k' x! e8 V
3 `" F! v5 H( c3 f  Z|第一个固定参数var1
% b  g3 K8 o: p/ c3 Y9 J|可变参数前的第一个固定参数var2
$ ]5 ^" W% e5 b, U% A5 B( n|可变参数的第一个参数; j" D& m7 D( ~; j% d4 f. f
|...
  C4 U: F* ~0 h% W- Y& R|可变参数的最后一个参数
# M* y7 A9 E7 X4 @|函数的倒数第二个固定参数var3% D" r) z- M5 Z, w- O& f/ H
|函数的最后一个固定参数var4' Z$ T/ Y% w) f- X: ^8 K) T
|...7 V% X9 {7 i' n1 t7 l
|函数的返回地址$ V, E2 O) s$ m2 E
|...7 Y+ @* F9 T/ K2 ~3 N# |* Y' {. K
|栈底    高地址
4 F4 K, P! Q& m* f$ z
0 A% m% M3 o  x/ a" o0 P# D' G0 l  ^2)使用可变参数所用到头文件和相关宏说明
4 n& y/ [1 K. o4 a3 ]0 U8 x9 @4 @& A; `: L
在此,以TC2.0编译器为参考对象来说明.
# @5 p6 _" r- ?* e可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.# w2 w  H1 E( H' @) Q: W; s
此文件为:
6 h% q; w) S! O0 E3 L0 C* V, o, t/* stdarg.h! z3 a: Q5 \) |  u
# ]* V# G3 L. I1 M8 `' e5 E
Definitions for ACCESSing parameters in functions that accept
2 |6 D1 v1 v% A: M2 H8 J4 Da variable number of arguments.
6 {/ n) L* y4 b4 |
0 K; h- w% R  E6 G2 _' }Copyright (c) Borland International 1987,1988
: o+ n9 u( ?' r, X/ Z* vAll Rights Reserved.
6 ^; Q4 {! b% X7 {( n* b*/+ p  o" b% E; w8 `
#if __STDC__/ E" {: F  c! H
#define _Cdecl5 c( s* {7 x5 ~: O6 N& S8 w
#else
, e( q( M$ M* a5 o#define _Cdecl cdecl! B  V9 U% D% \* T
#endif) }0 H7 ~4 M% }& x
) E/ A" O2 n0 T
#if !defined(__STDARG); ?2 ~: I8 N- i. w4 y4 ~/ @
#define __STDARG
3 M  \3 ^( B: L; {; Y. T( r8 L: h1 V4 c4 d  i2 s- K: Q
typedef void *va_list;
& o  [+ M; I) a- j( z; W
3 p& G; H5 {. t* l#define va_start(ap, parmN) (ap = ...)
; e# k# g$ r7 }; O& R: a#define va_arg(ap, type) (*((type *)(ap))++)
0 ^+ W/ f- I- ]: G; [% K#define va_end(ap); q. O' n4 L4 c
#define _va_ptr   (...)
  @  ]% u2 A( X$ e. n- a# ?) T#endif4 G/ q3 N- ~/ P, z6 A! ?

% A, o/ P/ T4 |. L9 j# g: M! T以上为"STDARG.H"的内容.9 J& U6 I; w2 K4 Y
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
: h! f" O' Z0 l8 _/ rva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,% J3 Q2 m, }* @. ?
parmN为可变参数的前面一个固定参数.
2 [+ i* N1 v6 F" J; d, ^va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.  P3 |; l1 L+ }
va_end(ap) 结束可变参数获取.0 H  a! A1 }' [% b7 s
. W9 ^6 o* I9 J
3)可变参数的使用实例
/ g+ W6 b% |$ {2 j, B8 x2 q
% n! c8 P, ?1 G0 E实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.+ ]9 k+ D3 w- z4 K- a# k3 M

0 W. _. Z' y* B- a# ^2 f#include<stdio.h>- h; Z. B1 L% `' R5 ^. r+ ^
#include<conio.h>
" W2 S4 d& }  \- {. D8 V* O#include<stdarg.h>
$ L5 ]$ Z( N4 t# E: Evoid tVarArg(int num,...);/*num为可变参数的个数*/
/ e0 P9 P5 h+ T1 ^. qint main(void)# q5 W  m& s% T* Z' Q( e
{& y( B% ^' P8 Z, B
clrscr();
  f# B  T. J: B0 C1 k! utVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");6 e' Z0 v8 R) k) Y) _0 }
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
/ {( ~$ K8 |& q3 t. egetch();: G: U1 M7 k! W& J5 f
return 0;
0 B% h% {9 S. P% v1 u1 V}9 {; e1 W1 l2 S/ p
void tVarArg(int num,...)" ]& l! V' y3 `
{
/ u# E+ j; n  ~/ C: |5 a! x; F+ pva_list argp;  /*定义一个指向可变参数的变量*/2 c8 s1 h- l8 p" Z7 A; C
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
. ^( n0 k) ]! b5 r2 w' L5 Z- S1 [while(--num>=0)3 R$ S" R# B2 H. B7 S
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
' J* `/ `$ l1 V    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
. U% G  ]( J) q& cva_end(argp);  /*结束可变参数获取*/
% x% d. U- n1 i8 ^) U/ w, creturn ;
7 W/ O* Z6 |5 |+ O6 o3 i$ V! c}
! L  o& l- v! e5 b6 q! y$ t
3 \6 w& w. R; r# h4)可变参数的使用需要注意的问题
# O( n- R/ d* ?$ h1 ^9 g" m
8 l0 g2 F2 f! \( D) C1.每个函数的可变参数至多有一个.
) H$ |0 k7 i3 |8 }/ \* e2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.; O! s$ D) U( D1 Q# l
3.可变参数的个数不确定,完全由程序约定.
; j4 Q. Q- }8 j1 k3 l% u; Q4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
% M& t$ m7 `0 N8 L% \; U$ U而printf()中不是实现了识别参数吗?那是因为函数
: |3 W2 L2 p' `) Q, O5 k1 f* lprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 6 ]6 ?  t' G: j7 g5 J
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 ' {. \" p$ c: Y" T8 g
过在自己的程序里作判断来实现的.
4 A) E) x, p( V$ F$ R6 y* v! ?' H5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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