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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
2 N% T) T: @/ K% x9 K9 @它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
: M/ ~6 o4 e& T$ T& [% F# E可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
! `0 \9 [3 x5 o  j2 X/ I( {定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有. e1 {! z$ }" o+ n  h
实际的名称与之相对应.% a, c- j4 ?# `0 Y0 W$ d
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.2 U+ |; o* k1 j9 k) z
然而,更多地自由,同样也加大操作上的难度.
6 X' T2 U2 y# g/ f0 ~以下就对可变参数的几个方面作一定的介绍.
' g) C  E: Q: R0 d+ n; b9 ~$ \1 H  Y6 Z
1)可变参数的存储形式.* _. N. K* h/ Z0 }- H/ [
2 X) t1 J4 V; j
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,$ g4 o8 u* ]" l$ l
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.( i, \5 C  b8 p
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,: j! g1 T& J9 m
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.2 N  l8 {+ }! S7 B6 I: |
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):" f; o& R/ Z/ L3 F. ?0 m; k
栈区:! n7 h1 l: ~" ?) l" f' F; v. S, a
, l+ b3 j& q5 T$ }
|栈顶             低地址3 x8 U) _  `, Q. C; A
  \6 e: d/ @$ H# v9 T4 _4 {4 K9 [
|第一个固定参数var17 O: @0 u- X+ a, H% |" y4 E7 m
|可变参数前的第一个固定参数var2
) I, N+ t, i7 p/ o9 Z|可变参数的第一个参数
$ c( U7 _3 L. ^* I! S|...% y* }# u5 b4 X. ~# i
|可变参数的最后一个参数
* j: ~3 A/ O" T" w|函数的倒数第二个固定参数var3
: y' y* n8 |5 D. M4 N* \|函数的最后一个固定参数var4' }8 r  @3 P1 q
|...
' Q& D: ^. a4 T+ P6 a2 f! x2 t|函数的返回地址
; q2 w% K' @& n( `3 ?# O; p1 Q|...2 X1 k. t6 y8 `( j0 A8 G
|栈底    高地址* C+ l) E% T( N+ K
, D; W8 J* @* ^7 k" f! ]
2)使用可变参数所用到头文件和相关宏说明
/ o% A" q8 }8 A0 Q/ ]. B  v0 |
, z: U) R. O) r0 X4 f在此,以TC2.0编译器为参考对象来说明.
9 ]% R1 z$ k; P# x2 ^' r) p可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.; ?2 W* r& z! j8 G6 W' X
此文件为:
8 m( A) e& j) F, B) {/* stdarg.h
% ^8 Z6 {$ r- a! S% K, G+ r  @, |# ]2 i& z8 t) ], _& {
Definitions for ACCESSing parameters in functions that accept
: D+ |1 k! e  ^1 ua variable number of arguments.: L$ A: b5 V% |' _! h- R$ \
5 A, }% R7 k2 h3 Z0 m- U: R& l
Copyright (c) Borland International 1987,1988
, U9 R1 I9 X$ N; i" n; zAll Rights Reserved.7 s% t% \8 p5 R, _' }6 c
*/
- Z& Z: i( c0 n5 U#if __STDC__8 I$ D9 \( V# C, i2 Y8 C
#define _Cdecl
8 [" j" r2 |+ B. H2 d#else
6 ]: }( j$ C( K, N, @- R! t% L#define _Cdecl cdecl
! L8 @1 T% a3 S( q6 N- m#endif; T. Z5 S8 ]0 X: ~8 u

& m* z6 Y. N% F5 K( [3 A- U#if !defined(__STDARG)
, s3 N1 I4 A: T( B- k#define __STDARG
: g5 U. Y* ~# _, r
' J! m2 t1 _6 \typedef void *va_list;
" O' O" y3 J  a0 c2 A# s' ?
$ |. d! ^$ p2 [9 U#define va_start(ap, parmN) (ap = ...)
- v  m; Z# o; G) h) P1 B#define va_arg(ap, type) (*((type *)(ap))++)
1 m/ h' K' q2 b& F: J. B4 i' r#define va_end(ap)9 p$ I$ k3 X2 r; I1 b3 O
#define _va_ptr   (...)
. W: k; s1 s/ F' K3 @0 j1 S#endif
: P) Z0 V" [- ^" P+ H( d6 g: @) H: @/ p. d8 k* f1 M
以上为"STDARG.H"的内容.7 u' `8 }/ i- o3 _
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;5 K! y4 m4 T  q% Z- |: U
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,1 d9 i# S+ D+ T" w3 i: W. u
parmN为可变参数的前面一个固定参数.3 A; n# e& O6 n) O. r: H
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.2 D+ @  w; W* N! n0 R. T, h
va_end(ap) 结束可变参数获取.# y. r: l0 u+ G: S9 Z  B

: p/ l$ ~/ A0 s8 v3)可变参数的使用实例: c" Y' C! q: M) m/ }% ?

7 J1 l" x5 ^6 T* W实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.% Z& t' w; `5 H

, ]( n8 u& r1 e) S( m$ v) a, [#include<stdio.h>
' W% g6 o/ A8 x) h! ~3 u( U#include<conio.h>$ C( v- K: f5 |6 `- D
#include<stdarg.h>& O; _4 R2 v' H. s) T
void tVarArg(int num,...);/*num为可变参数的个数*/, t& D0 b- h( m, U
int main(void)3 [) d: J# ^- s5 b
{! m7 `+ ]2 c2 Q
clrscr();3 w( v! o( j$ h0 m9 s' H
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
8 L/ w6 z% V% U) [3 CtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
5 L' F) v, q% h5 Ngetch();, C) J  J) _5 K2 L8 I
return 0;; r. S  W0 `7 ]# y5 u( M
}3 S8 M  V( I' j& C$ R& a; W
void tVarArg(int num,...)* V. |( y9 X& I( s1 b' }9 k5 w
{
- I9 X) g. e! D9 j6 Sva_list argp;  /*定义一个指向可变参数的变量*/
* ^6 G3 v4 D0 Y9 [/ G+ L2 Hva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/4 \7 p- P" p  r1 z3 c/ `9 i# ?1 L0 F
while(--num>=0)$ F* }$ N4 [5 d, _0 y
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,) T' g5 t2 [" ~* B: p7 i
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
. O) f& J  L( p! G4 Gva_end(argp);  /*结束可变参数获取*/
+ q% x1 A, G/ `, x' Y! ~) ?1 Lreturn ;
4 N: J1 z5 u! _: |/ Q}
0 q* E, I$ E' B. L8 E' s, p) z9 C$ w" E( E: D, x7 |/ N
4)可变参数的使用需要注意的问题
3 f( e# L1 c4 R* {' m" F  c" {: I$ e: F& _" c
1.每个函数的可变参数至多有一个.
0 y6 W- Y1 z& C, ~( c4 c) d2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.' |- M, ?- M8 j+ b1 n
3.可变参数的个数不确定,完全由程序约定.2 G+ x; @" N4 D# ~' l% |7 a
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.* X7 o* Q& s9 C1 [. T& R
而printf()中不是实现了识别参数吗?那是因为函数
  x/ L# S) O/ u8 o' jprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
3 P+ w- F% P$ J的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
, p+ K8 L  ]! H6 ?& f1 J& \" d. v过在自己的程序里作判断来实现的.
6 e& \* q: q2 O8 U( T- I8 c5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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