标题:
函数的可变参数详谈
[打印本页]
作者:
zw2004
时间:
2008-1-21 19:42
标题:
函数的可变参数详谈
可变参数的英文表示为:variable argument.
5 V2 D5 Y5 X2 N; I9 @# ^ ]
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
3 S+ w* p# A! n6 q! e6 K/ T4 m" f
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
$ B9 R4 G0 a: [4 X2 M0 e
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
9 C Q# _% E) p3 b4 t9 n
实际的名称与之相对应.
* Y" C8 |6 W9 \* Y5 o2 Q
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
; n z R+ Z/ R! @3 i
然而,更多地自由,同样也加大操作上的难度.
4 S. A+ S% d! X% `+ F3 n$ N
以下就对可变参数的几个方面作一定的介绍.
. T* a+ \7 U8 a/ b; [) w. O, R
' ^/ F( p0 n$ k
1)可变参数的存储形式.
; [" i; i+ [- x* O G3 Q7 [. c. d
6 B$ y3 X5 z: u' M) Z) C$ x
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
4 w9 M; \: H" [# i' f+ }0 Q- ]
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
8 c" w; k: e% x( a: W, z1 Y
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
. o' ^" c$ Y7 A* u
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
8 f) C- K/ k$ J3 y8 ~0 Y' X9 s2 C' g
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
9 ] E7 @7 ?: n0 u& V# y
栈区:
: H! H- H. n) O3 p( g. u. o; D$ E
' C" S+ O1 j6 t1 I9 }1 V! k* C
|栈顶 低地址
7 C% W- U( h6 r5 C
/ m v; H3 ^* {: ^; c
|第一个固定参数var1
5 m. b- t6 V$ X
|可变参数前的第一个固定参数var2
% ? r' ]+ x0 O( [ i) a% V
|可变参数的第一个参数
* H6 @- F* [9 W9 V M, C4 x
|...
1 w# f+ Z% n& q" g
|可变参数的最后一个参数
7 Z! r4 e9 S: g) f
|函数的倒数第二个固定参数var3
2 e; b, D0 u9 Z# W
|函数的最后一个固定参数var4
4 V, E- }7 B4 F2 D
|...
5 K% B5 a' w8 I$ F7 Y9 g4 [5 b
|函数的返回地址
; \! T S3 y, _1 K6 [
|...
5 w9 o: Q) L# ^# \; J: @
|栈底 高地址
* ?* b& T, `$ z B5 n0 t
# {$ b _) d" e3 f9 d/ m% \ e
2)使用可变参数所用到头文件和相关宏说明
* M& }9 K; k4 c( ?9 S- \
4 g( Q+ x+ }( P
在此,以TC2.0编译器为参考对象来说明.
9 J3 D8 {& Z: @: z6 x
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
2 U+ ?$ x: {7 W) B2 f O
此文件为:
, t. C5 `& e7 j9 I
/* stdarg.h
% j# e5 i1 @: Y
% l$ ^0 I$ d0 |) ^! r4 T4 P
Definitions for ACCESSing parameters in functions that accept
: q( d: y5 v& _6 K) k. b
a variable number of arguments.
& [1 ^# z/ d9 N7 i6 y$ W
; s* @% A5 j p, B& J
Copyright (c) Borland International 1987,1988
/ R, z3 t6 r6 t/ R% j
All Rights Reserved.
) L+ a$ A0 Q6 \' ]' A& B" J1 ?
*/
5 f' |9 E! J, ]# H4 \0 E% R' b
#if __STDC__
: g6 E4 h; O3 o1 X! B3 w" S
#define _Cdecl
& g! |6 P. J' F4 F
#else
0 _4 z9 g5 ~1 k% n c( O3 t- T' p
#define _Cdecl cdecl
1 w" Y, m% F, O4 `% P# S! T
#endif
$ G' u; N+ o& ]8 H8 K! l
* U7 Z* N4 u. [3 t; q1 k
#if !defined(__STDARG)
: s6 P, ^" {" I8 ]0 D
#define __STDARG
0 m' S0 }2 j: v, a; z# ^8 V
) u: A* z5 `6 y5 M7 [6 F1 ^* C
typedef void *va_list;
5 `& m& N0 M9 c; K3 `3 h
! r$ s5 e! e* W. m
#define va_start(ap, parmN) (ap = ...)
9 }" G% L) l- ?/ i
#define va_arg(ap, type) (*((type *)(ap))++)
; p$ K+ v! O0 a6 }% f3 U
#define va_end(ap)
9 b1 B! r1 \ S& T# N0 W( ^
#define _va_ptr (...)
' G) u; S# ~/ t+ X& [
#endif
* P( z% i& T7 H# g* U+ a" x4 L$ _
8 m5 D h# ?- F2 m* e- x
以上为"STDARG.H"的内容.
" C2 P- \, C0 j
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
' h/ h4 ~- u7 B. {
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
; l/ _6 I' u2 j4 {
parmN为可变参数的前面一个固定参数.
* A' T5 k! M) P( o/ f4 j
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
, A' f7 [+ j+ I, w4 V8 Y
va_end(ap) 结束可变参数获取.
2 A8 x6 E! _" s& l! x$ z
* w, C+ G. X5 [* b4 i
3)可变参数的使用实例
1 @. u q" Z" ^% q( x6 i
8 O/ A: Y$ Y9 f3 ~5 k) l
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
# G& k o# N3 K* [3 C
9 {6 t: ]: @# K: G
#include<stdio.h>
4 M: C5 B6 A- A
#include<conio.h>
+ o4 T* r" |5 w6 q/ H% ?$ B
#include<stdarg.h>
+ E6 c+ s! ^3 B" @" g
void tVarArg(int num,...);/*num为可变参数的个数*/
7 Q/ V+ l% f5 m1 k6 F1 @4 y( c
int main(void)
3 E+ F5 _' `- s( F6 C" d' t+ c
{
7 d+ n' p9 Q, H E0 k6 h
clrscr();
t, B5 P3 @& l* z2 u! O
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
! T. }: }" x4 ~% Z; S
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
$ _; T5 E. F- k) W% G4 X
getch();
* _+ Y9 h) x% s
return 0;
' i m) n7 B/ t! O v9 b/ S
}
+ G; `5 H/ E" @" G9 u D
void tVarArg(int num,...)
: d4 `# @; R* {
{
. ?9 S8 l% j' Q& J& U k
va_list argp; /*定义一个指向可变参数的变量*/
, ~5 w: X6 O; c/ p
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
3 j( x. `9 C: E, L& b- C3 h) _
while(--num>=0)
% R' @ x; D1 s- e2 v9 w P
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
9 X) m0 @; ]! c$ H4 a8 H- u
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
* h0 S8 Z- Z+ F( W/ Y' p
va_end(argp); /*结束可变参数获取*/
3 q, q1 s0 P: k
return ;
* ?0 ^( D/ B- d3 [+ y
}
& T) B& O$ G+ b! K9 u( \8 r/ z
0 | P% q7 b9 `3 Z# X M
4)可变参数的使用需要注意的问题
. ?: t2 D. g3 l2 e! v$ o
~3 A. g m K
1.每个函数的可变参数至多有一个.
% P$ c8 | v, V
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
) p0 J: e+ X3 o9 I
3.可变参数的个数不确定,完全由程序约定.
. g/ {# A8 O5 u: K- D, R/ c
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
, s4 a# o2 ^' I: B A2 C
而printf()中不是实现了识别参数吗?那是因为函数
* D7 O' y( E; d( o
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
4 n8 i% a) h* t
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
# e) i+ H3 W3 ^) ^) u
过在自己的程序里作判断来实现的.
1 k( U( B7 ~4 R. U) K
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.
欢迎光临 捌玖网络工作室 (http://89w.org/)
Powered by Discuz! 7.2