返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.& g9 C( B9 K+ v- u' I* u9 {+ s% n
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
; z0 U6 Z, j& a3 b可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
0 Q3 l) h5 e( s$ Q0 e定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有, G- r* @8 `" f1 k4 ?0 w
实际的名称与之相对应.( Q; c, E0 w1 O
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.# q1 n% {# G* E; {  u* ^: S# ?/ A8 m
然而,更多地自由,同样也加大操作上的难度.0 p) d% z4 I+ @3 L- T
以下就对可变参数的几个方面作一定的介绍.1 e2 y  f# ]) A  @; N% ^
2 h" v, N5 y, m. f) `
1)可变参数的存储形式.
. L" s1 ^9 \0 @
+ z4 _0 t" O$ N大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,+ S  A. ~4 T+ G) k
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.0 J7 h3 m# ^1 f! ^6 [, Z0 \- |* N
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,6 D: d. u5 k4 m4 Z  w, ]3 t$ ~0 m- W2 N
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
  K. x1 m% n4 u5 X: ?6 o因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):- k7 u) B! v  }0 R
栈区:
0 b; z: N# O2 f% B; x2 _
7 X& ^( d. A! y. \3 P|栈顶             低地址
7 R" e% l0 b6 @# b7 T5 z$ ?: e
# A3 `5 x8 o! W4 `: M. M" b|第一个固定参数var1+ i0 N( z1 ?7 M; ?7 P& r& b- d- `
|可变参数前的第一个固定参数var2& j1 i+ j2 j+ p  [$ |
|可变参数的第一个参数) U6 s0 K+ ?# u! v! x" E. `8 R3 c
|...
6 D. K7 P3 d1 ?6 t- C% H|可变参数的最后一个参数
, x' o, Z- e4 J' Q( u6 s/ w|函数的倒数第二个固定参数var37 X, o2 T  I! K
|函数的最后一个固定参数var47 @# Y0 J- N$ D6 R3 `
|...
% x3 P% e; F" }$ K|函数的返回地址) f. d! P8 w: x9 T; k1 B/ c
|...
+ b( J8 @9 P# N) p' h|栈底    高地址; t6 X' S5 `! K4 b: {/ L
  y5 ~) K) u/ J# s) ~
2)使用可变参数所用到头文件和相关宏说明
5 v5 j2 H) m& v, n% h0 q' F! L
( U# i: ~8 C! l1 ~( c6 g在此,以TC2.0编译器为参考对象来说明.
7 c8 U) Y% P# V4 d可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.0 Y2 J% M! K* U. _6 K! _" _
此文件为:
& }4 k5 A, I- o( c/* stdarg.h
  y: {. C# ?, r; t8 E9 y1 f0 L3 N4 Z3 s' J/ o- s6 p, o9 B7 q
Definitions for ACCESSing parameters in functions that accept% A3 D) R5 @; A1 X" g: k$ a
a variable number of arguments.
3 l$ j# n, j9 P6 X' a
7 i& P- B6 y# ~! O3 j) [5 eCopyright (c) Borland International 1987,1988
+ o. H# H. y2 a2 {; j/ W/ BAll Rights Reserved./ W4 A8 x/ r; p, |8 G
*/5 Z7 v! X* `3 ~$ M
#if __STDC__. Q+ B0 g! v7 K0 D  `# T
#define _Cdecl
; U& q2 }& [/ ]  [, w$ q#else0 M3 P8 D/ Z5 ~$ W7 c
#define _Cdecl cdecl
: E! U0 v& l! x4 K  u2 q#endif
- q- D. N! q# {: U" b/ G5 Z# a; N: s' g
#if !defined(__STDARG): F. q5 c0 G* n: ?4 H8 R
#define __STDARG
1 n5 y6 L: i& i4 S% a4 ^( g" V$ q  v$ [& k- `# C
typedef void *va_list;0 g; g( l* h% s- Q# J; A
+ Z, W! q+ P  ^# f/ {( g7 b) w% ~
#define va_start(ap, parmN) (ap = ...)
: D( u9 O2 S( `$ q; m#define va_arg(ap, type) (*((type *)(ap))++)
" M. v) f4 y5 x. K#define va_end(ap)! @5 N" D4 L+ j! l9 m4 k5 f
#define _va_ptr   (...)
& a1 K- @5 P4 [" X0 f4 W#endif
7 K8 t6 R4 z, M' [: {; v- c
. g! @8 Y7 g' ~. K4 h以上为"STDARG.H"的内容.
3 C- Y8 o* C4 n$ `该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
8 m6 r7 b- L) M1 e% Pva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
) f% I* J5 V2 t, rparmN为可变参数的前面一个固定参数.; n! a. ~1 S# k1 f5 V' u0 X
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
0 Y/ ]: ^1 G' r5 R; Vva_end(ap) 结束可变参数获取.
& w, @8 u5 Q2 s( d2 b2 B
5 s9 j- f( z) c  z+ G7 V  u3 z3)可变参数的使用实例5 x8 U4 l$ h  N& W/ ^3 U

( K( H3 v3 P. }- ?. O4 _实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
* D1 r2 O% |; k8 B
; N( t) z$ k6 l: @; i6 q#include<stdio.h>
. _( @, G, E3 e$ F! D3 I#include<conio.h>: [: A( Q2 N% m. ?6 G
#include<stdarg.h>4 T* h3 i( W! T9 ~2 Q
void tVarArg(int num,...);/*num为可变参数的个数*/( A/ e0 o4 K# A: p5 ^/ R9 S8 s+ m
int main(void)
6 q) i+ d# O. `. U! w% b; Q1 X{& S, E# I( Q$ q
clrscr();: ~7 N3 m) |% K$ j7 A- G, b4 q4 P* J+ b
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");: Q) M) t) a, d( T7 t% H# x
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");% c/ @. t4 i6 ]2 j5 F0 F3 X
getch();, K3 k7 `2 x9 p/ h& P5 x8 Z/ n: ?
return 0;
7 O- }' D& l& {! @9 H, K! e4 [" H}
9 f( G" H2 y; c1 c! X* Qvoid tVarArg(int num,...)
6 R  c# o, N: H* n* b{+ ~# C" ~& I. {; N6 S
va_list argp;  /*定义一个指向可变参数的变量*/' [! d- ~" T3 x( C9 l
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
  T: w; D+ N+ E* gwhile(--num>=0)
$ P* u& ~" T& \  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,1 e" |6 ^/ V' m
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/) ^+ W, S( F/ \3 H( h2 l0 I
va_end(argp);  /*结束可变参数获取*/1 Y" p, D0 B# o
return ;
$ Q* ]2 d, X7 q/ j}
2 l% s7 S4 ?5 q% ~! ^! C
* V, J3 W: Q: s+ M" l& s! S- J; Q& p4)可变参数的使用需要注意的问题
* r( V$ G8 M/ K3 l
% c# m9 k# {: p$ _! i# X% [& V4 e( M1.每个函数的可变参数至多有一个.
, l2 g" q1 W& i* X. Q+ q2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
+ I8 X  A* V$ D$ j3.可变参数的个数不确定,完全由程序约定.
% }$ N: @" X# x4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.3 R/ ]! I/ m4 m0 I
而printf()中不是实现了识别参数吗?那是因为函数
1 }& x$ L+ h9 jprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg   [3 d) T' c# @' \+ a& m+ I
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 & i3 a& M4 ]8 i6 v; R# H: Y" h
过在自己的程序里作判断来实现的.
* ]1 t. m* k1 C. m6 R5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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