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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
2 X2 x7 y @2 o K* F! [它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.) c% n+ H" K1 x2 ]
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不6 I# j& f. h( g h
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有( E5 V9 H+ _% m" s; n; s( |
实际的名称与之相对应.
q, I! }4 S q- X4 l由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
9 S9 q7 N# _2 M( h' p" Z然而,更多地自由,同样也加大操作上的难度.
: @/ t+ p9 Q- b6 p5 n以下就对可变参数的几个方面作一定的介绍.0 e7 t% Q y2 c& l1 k
, M( R: I4 D6 e1)可变参数的存储形式.% Z, z# d5 Q- Y+ P$ p) k% ~
, o. L. Y# k h3 P0 {
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,$ g6 b+ z' T4 b* N* R# y) _8 r
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
2 W4 w& y) y5 W1 O2 y9 S6 M在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
! o6 P# ]% V( ], T这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
1 @1 i$ y0 _* j* O" o因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
* h4 ?6 b4 _1 {& A/ P栈区:. }4 Q4 E2 h( |
' ~ i$ w5 N( [( m: h7 ~|栈顶 低地址
& {: d, @ a n- C4 y# x2 ?4 L2 h. O# r" r n, _( u9 P0 H3 U
|第一个固定参数var1
9 D2 ~: J0 J1 N1 q|可变参数前的第一个固定参数var2
2 K6 C" U' a9 a/ g% z/ O|可变参数的第一个参数2 V- o! I3 F0 U0 U) p
|...$ p) @7 r# U# l- {% `2 q8 r4 H
|可变参数的最后一个参数
! Y6 Z u( k+ ?|函数的倒数第二个固定参数var3
4 P- j0 c: x. U0 i; z0 ^|函数的最后一个固定参数var4" Z$ U# Y/ Q3 U! s* _
|...
0 _- n- U0 D A* i! {6 ]|函数的返回地址6 y3 T5 p# o+ H% J; t- `6 j [
|...
7 L8 D6 k! Q1 l! Y2 @|栈底 高地址
; T( Q; m0 b' ?) O; A r0 e3 z) v5 C4 I" Z& o
2)使用可变参数所用到头文件和相关宏说明
" \' U! R6 ^0 W
2 D+ C" j6 Q4 z8 l在此,以TC2.0编译器为参考对象来说明.
4 W* X8 \8 h; ^& }7 e! o# ?6 M" E9 v, k可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
/ [( z$ ?% `! k y$ i9 ~9 C此文件为:/ O6 @( |* K, W" D9 o" y2 Z
/* stdarg.h6 p/ k1 i' ` p. ]' r4 |5 ^
% d {. @& w- r4 e4 `6 E$ GDefinitions for ACCESSing parameters in functions that accept( K# c1 A `4 v$ ]- I
a variable number of arguments.
+ R% y) A/ @4 |; V4 R; b
5 N; e" U( H! K3 \# B5 N. T$ QCopyright (c) Borland International 1987,1988/ W8 v, b/ C1 ?3 Y& w0 X9 F( p+ k
All Rights Reserved.
9 L) ^% M- p$ L, j7 v*/7 n5 h) _7 D" V, d! o
#if __STDC__
/ z5 c+ q& \, I#define _Cdecl9 @% s Z4 O; p+ r
#else j/ ]* R" q; v- j5 ~: {
#define _Cdecl cdecl$ J) e# G- ]2 l- n m& W- `, V
#endif
) F; b8 {! h$ c9 M6 f- Z9 x$ Y" b) o" b. }( |
#if !defined(__STDARG)7 N" c: g8 N( [2 L
#define __STDARG! @* a4 F4 t1 l) i3 d
1 `/ X5 z9 q4 T
typedef void *va_list;) B+ R4 T0 d1 f+ i5 H
' q2 K J1 ^0 z7 V T* Y# \#define va_start(ap, parmN) (ap = ...)+ A! P" Q0 y2 h7 d' O
#define va_arg(ap, type) (*((type *)(ap))++)
- k+ J0 B) x$ c, i3 I#define va_end(ap)
! M1 ?8 W' k) g#define _va_ptr (...)
- x8 z: S( _6 R% K8 v#endif! m B5 V# d& J+ O) {
7 Q: c" F# l, b
以上为"STDARG.H"的内容.6 |- O! d6 w9 e7 Z q$ y. h
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;5 ^0 X7 c; ^5 l$ H3 f0 j
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
! A7 `& E1 A' m! H8 AparmN为可变参数的前面一个固定参数./ W5 q' i% y% C) F* m( e
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.$ S+ m( ~/ [' V
va_end(ap) 结束可变参数获取.
) D# y1 x' g8 |- m2 V! T2 d7 R) J
7 z8 f7 c$ ^; `* K3)可变参数的使用实例 _1 a, I7 D* \4 ]6 i
/ W# f9 \# Y% \5 t
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.9 e! S/ P! [& n0 ~4 d
6 k* M) p: {+ D#include<stdio.h>5 }- C+ O9 e6 ]
#include<conio.h>% g9 O' |0 |5 y; r& ^0 _' Z( l
#include<stdarg.h>" u$ B+ R/ F3 m9 X6 U2 i
void tVarArg(int num,...);/*num为可变参数的个数*/
v& ]9 y( J4 R; X0 tint main(void)
4 @" h/ `/ L! B# x{% u8 C9 q3 }& s) G9 [
clrscr();
) D0 ` q# h- Y! k; stVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
! Z- M X0 V& S( CtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
% d* m$ |! x' P4 s- b# Egetch();
# a! m& d4 n, k5 Nreturn 0;" x/ W: E8 H* m/ Q
}/ _9 c1 X! y' L0 @" K
void tVarArg(int num,...)8 V8 a& i. s4 P7 I1 L
{
8 s' [4 |* }) t, qva_list argp; /*定义一个指向可变参数的变量*/, c( W5 `) Z+ H8 }1 l0 ]
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
; N( K3 h+ m$ A: S3 Kwhile(--num>=0)+ i/ ]: Q$ {7 I: m; V9 o) k
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,% `$ D3 n' Q( `! c6 D5 s5 w
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/6 X4 z& L. [' ~& B0 P3 r7 ~) Z
va_end(argp); /*结束可变参数获取*/
! d" T3 M8 M* h5 @; Dreturn ;& W( j6 t4 S& ^, v
}2 j% m" Y2 g. Z* x
" P. j! y N" S4)可变参数的使用需要注意的问题
( Q6 \7 v J! E0 N2 C
! ` {, [: p: w2 }1.每个函数的可变参数至多有一个.
2 K' p8 U& i) V: f2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
3 D' n% x) G* a9 ]: }3.可变参数的个数不确定,完全由程序约定.2 z8 m% y( m* |# C
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.) k# q4 C+ _4 v& B: u
而printf()中不是实现了识别参数吗?那是因为函数
" Q: p6 W9 }) v% v+ ^printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
7 C0 o, G* G$ h) V* G3 u- e的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 , W. v1 Z1 P# a- C+ c
过在自己的程序里作判断来实现的. 7 U3 a2 v" L4 ^. N! ^ }
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|