  
- UID
- 133
- 帖子
- 51
- 精华
- 1
- 积分
- 186
- 金币
- 55
- 威望
- 2
- 贡献
- 0

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
( k0 j# a- g3 p. _7 P, ]它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
. F0 M. B. I N7 C5 D" E O可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
+ n5 H7 k7 S& H, ^. a: o定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
0 K2 ^" @" D8 i7 U& c, V' q实际的名称与之相对应.
- f2 M$ Z8 W9 ~7 z- `2 Q由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
' A1 S4 R* c4 X R, }. v然而,更多地自由,同样也加大操作上的难度.
+ U6 S4 J! c' y2 L2 ]8 E$ n, `6 U以下就对可变参数的几个方面作一定的介绍.7 H+ R7 d: Q$ Y8 t' K
( p& D8 J. P2 p1)可变参数的存储形式.
; F0 _9 y6 a' t4 e* J$ L( K8 A' @. S1 x- ]0 n
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,: _0 A; ]- r1 k/ t4 x/ \+ P
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.% u. p: u+ A: u
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,6 ]% l) Z/ c8 s: e/ k N+ U
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.8 T0 z7 ~8 S! u' c: T, I* A6 v
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):/ P# `$ u/ v7 o, a
栈区:$ F C; x2 w4 Y ^; ]
! p; y [2 f9 u. G, T; n5 u t|栈顶 低地址
1 H$ T3 b8 u6 q7 X/ x! z% k+ `: u$ W4 C% `( [9 v
|第一个固定参数var17 v1 V N& ?5 l9 x( |4 G
|可变参数前的第一个固定参数var2: N& }5 R5 R7 ~# x- ^
|可变参数的第一个参数2 P1 u: k$ D2 |! h7 G0 S
|...0 }0 q- G* U0 r$ W$ Y+ X
|可变参数的最后一个参数7 c! c/ x$ @8 \) J( q! G
|函数的倒数第二个固定参数var3
+ F+ q; G6 m7 a6 r|函数的最后一个固定参数var40 ?5 q% Y& n2 ?7 ?
|...
1 G& ~, ?: y9 k0 @3 m|函数的返回地址% g" r$ v! C# \. @' _" \8 h
|...
/ @1 j( \' z4 `1 j9 F& F9 n|栈底 高地址. a# s& W3 G" x W& C6 d: s
/ W q3 u' a9 w+ v$ Z: }$ B2)使用可变参数所用到头文件和相关宏说明
# J3 [' s4 {- A U1 M* \$ l- N9 e, }8 _( [* Y6 d
在此,以TC2.0编译器为参考对象来说明.
' ?: R! N, d4 p6 u3 Q; B4 W1 R可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.. }3 O9 F' `" w- }
此文件为:
6 b Q: I, t! ^8 o/* stdarg.h
) R+ a* x2 F$ J9 c
2 V5 c; l6 o. M& R2 H% @# X/ xDefinitions for ACCESSing parameters in functions that accept4 X. s( P+ H5 J6 {
a variable number of arguments.
* `* s& ~$ O+ n j$ }/ }& g/ g/ ~, b: A
Copyright (c) Borland International 1987,19885 t, j; g; {: i) P* {7 y( h
All Rights Reserved.
7 c' a ?* H1 p: @ m0 q2 @" u*/' R. q3 l. L* G! P y- t9 T
#if __STDC__
) I: D6 \$ E. m# t#define _Cdecl
) F1 u9 N. ?1 D: @#else
S% l* B9 f, \& b6 P ]" K#define _Cdecl cdecl9 e! t' R/ T( P
#endif+ ~# l: @- Z: |1 I# X3 j# R0 e
* r$ s, ~9 y" s#if !defined(__STDARG)! \! A2 J9 i: W3 X
#define __STDARG0 i: k" `7 |9 Q6 Z: `* {: ^5 \
1 e* H) D) r3 itypedef void *va_list;" o5 p) b' l% Q, }9 g" ?2 F4 R
! w& `8 g, E( D1 Q9 N4 M
#define va_start(ap, parmN) (ap = ...)
4 N7 C# b/ W! A1 `, Y#define va_arg(ap, type) (*((type *)(ap))++)/ v6 C9 [. D0 X+ A/ e* T6 Z
#define va_end(ap)( p2 a+ l) c- u& e% @) [$ T' N
#define _va_ptr (...)
2 E, W- \8 Z" K4 J#endif9 R7 a( l5 ^! g8 v
; W l Q9 V: e5 G6 k1 z, u以上为"STDARG.H"的内容.8 C, |, q1 G# c; K
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;0 q6 r5 u, p3 W5 p
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
) G( L8 F- ]7 e HparmN为可变参数的前面一个固定参数.
7 ^: L! d w6 q& j6 hva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
! h: @( ^2 `( R/ W! a7 e Z" C _ uva_end(ap) 结束可变参数获取.
8 \8 {$ U6 a, k. x+ z; K# G
/ m% N9 h9 D. w' y3)可变参数的使用实例
$ v* }5 Z* I3 T# i
& @& b* d! F7 T1 e* A+ Z6 x! f" p实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
* j( y8 U- D0 V/ t* ?: d' z+ v4 U2 L! q1 s( s& |9 J
#include<stdio.h>
% {- y `) T* B7 m3 t#include<conio.h>
. P3 b' W6 r4 D#include<stdarg.h>7 q* {! `; u8 R0 p y
void tVarArg(int num,...);/*num为可变参数的个数*/
9 j. f# u6 n* R& Mint main(void) {# P, g1 k$ v) p- b/ _: D
{; a7 f$ N d, c$ N+ E
clrscr();
8 E, M r" c; u& y, V* UtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
1 O& o6 V4 x4 ftVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
K" D# n& r1 ~+ W+ j' Lgetch();/ H! u; e1 x8 @5 m. t
return 0;4 A$ u( y# L4 A' U: N M
}. T! l$ \( H8 e, D/ O0 y
void tVarArg(int num,...)
) |* |. O! s( r% s, t4 Z) X{
, z, N5 w& g8 |! y4 n! iva_list argp; /*定义一个指向可变参数的变量*/
- `! @) j! R3 Rva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
+ y9 x2 B* O9 E6 ]8 R! x! h2 a) dwhile(--num>=0)& _$ c6 U: u6 m' S
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,/ D. g4 D' g; X- r: j
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/ T2 i1 A- u8 M4 f
va_end(argp); /*结束可变参数获取*/7 R2 y4 d2 ^) \. r. r
return ;
7 U( j+ [; H$ Y% ~# T! \}( [' X3 f' K. O6 o2 i( S& k% E
1 V9 k c* d3 j; w7 |3 E
4)可变参数的使用需要注意的问题
" \- w! u' Y- x& n4 P5 {' |
8 @ S' q2 x$ y, m) g; l' e$ {1.每个函数的可变参数至多有一个.
4 C. u$ R! Z" K, `/ D4 Z3 [2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.# ^8 u% Y$ q2 k1 x3 ` f
3.可变参数的个数不确定,完全由程序约定." W) q- F2 m/ I
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.4 s) r, t4 F/ l+ i* M( t& ^
而printf()中不是实现了识别参数吗?那是因为函数 / s6 i. Q' u3 |1 ]4 J
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg , @, o$ B3 ^5 G9 Y
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
4 w1 L+ C% ]4 _! B* |过在自己的程序里作判断来实现的.
& ~9 V& g, ]: h R3 P! ?8 Q- F5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|