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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
9 _9 S# Y% ]9 R1 Q' k它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
. X4 M+ I/ ?: d" Q5 ~可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不! h5 _* l! J, `
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
4 g; |1 S+ O. K, r1 K0 Q0 z实际的名称与之相对应.) ]; b8 `# ^/ G
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.7 W! j: y& Z# |1 j5 {
然而,更多地自由,同样也加大操作上的难度.
0 z- h5 F3 K8 ]# q以下就对可变参数的几个方面作一定的介绍.
( \. g, U* D8 u: f% P+ V" `+ v1 ]  M; N( N  u/ u
1)可变参数的存储形式.
& j* K8 L- P8 p( C( k: z3 y6 ], e! w
! P* A8 d5 T+ S' ]$ w: `大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
! S# L) z8 t0 ^存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.6 k( z8 H1 u: Z- v
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
9 E# k. ]  ~. y6 x这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
7 P' t0 e; o$ h$ E1 j- e1 E因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
  |( b) j0 D3 g. P; C& ~栈区:* ?* K0 C  h4 G- }9 M3 o, Z
2 _; b  g+ L% Z/ Q& g# Y/ g4 o/ `
|栈顶             低地址
; K1 O& b8 Q1 U% H1 a9 J1 g5 h" ~. {& a" E8 G# x
|第一个固定参数var1
4 C! @8 n9 o3 \  Q2 s|可变参数前的第一个固定参数var20 k# f# j% G7 N8 L: k/ {2 M0 \" V
|可变参数的第一个参数* Q. u0 O) _' X+ `+ s* {6 l9 M9 ?; Q
|...9 U, M' H4 y4 r% l0 x) F( g
|可变参数的最后一个参数
% y# b2 m: g& Y0 w1 M% T: X|函数的倒数第二个固定参数var30 w" R. L  n$ v2 J4 I
|函数的最后一个固定参数var4- s9 f3 x# \& ~
|...
8 r" q  {# `2 e0 s" k6 A; t|函数的返回地址
4 d: L- S& [7 W5 p, d3 Z|...
3 J. f( A1 E6 S|栈底    高地址
2 P- i5 o0 A6 a$ m! ?+ Z" X5 u) x! e% N! z4 B) ?
2)使用可变参数所用到头文件和相关宏说明
' X' ]$ C+ @6 I) |2 \* G
8 e& a( f6 X3 A7 V: Q3 g# R. j在此,以TC2.0编译器为参考对象来说明.
  l' y2 W- k0 n0 d1 u* R可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.2 G8 A8 T) p( i' k" }: S
此文件为:$ c( r- k' e- d1 m' M1 z/ r2 R
/* stdarg.h
2 j. @/ |2 \4 N. v4 ^5 G/ w+ u9 [
Definitions for ACCESSing parameters in functions that accept, x: ?8 ^( B: c3 p! f! {* [
a variable number of arguments.
, F8 K( n( C& u/ d* E" z+ }0 a' S0 x' V- K' L: {/ l
Copyright (c) Borland International 1987,1988% o6 {3 `/ k' o& X9 k4 E' E
All Rights Reserved.
2 T# T# F. ^1 u8 ?& F/ ~*/
8 s+ X% r% x. Q$ \) m4 }" X; s#if __STDC__
' s9 w% c8 |$ {: b#define _Cdecl
, B* J" R: v4 \. ?6 G#else1 o) O  P" D  A3 y7 {
#define _Cdecl cdecl9 ?6 j/ X9 ?9 T7 A( H$ g8 S  ^
#endif
) n) b3 v/ f2 S# }) R
( O& K: ^3 N. G6 w. m+ `  X1 T. Y#if !defined(__STDARG)
" h+ J3 U8 c( e" E#define __STDARG
( J1 C9 p' D" k4 b
/ w$ Q& k+ U0 E& q5 L/ [typedef void *va_list;
9 Q8 ?7 v! e: l+ F8 ]: F8 V5 Q1 w! Y/ D- r/ n
#define va_start(ap, parmN) (ap = ...)- n7 T% b. h  G1 Y! f
#define va_arg(ap, type) (*((type *)(ap))++)
2 l! u: L+ R* t" e# O  n0 i#define va_end(ap)
- e' I0 V4 B/ n9 M, O# N" W#define _va_ptr   (...)- G3 w' r5 L7 ]+ v4 m# R
#endif
" l* w0 h! Z2 i- O# H
/ \# M& t: z1 E8 ~; ^  }# }以上为"STDARG.H"的内容.4 r7 w* n  |0 d# D/ F* X
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;, l4 R" X1 T% e4 v( z. _3 @
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,+ Q5 Z) L0 \- _' c! [
parmN为可变参数的前面一个固定参数.
( S+ K3 w0 Y3 d# Jva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
4 {3 v0 X. Q- Jva_end(ap) 结束可变参数获取.4 K. g4 a5 Z2 J
- d% \+ O  g+ x% B: i) y
3)可变参数的使用实例4 h$ B8 f& z& U2 Q2 R/ |6 X+ p

+ Y  \9 q- K2 y实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.9 a: \3 n& ^' K1 p" j
, h$ M" P3 Z! X" a8 Y! R/ w% S
#include<stdio.h>/ {- j9 s/ z( I) h/ X# w
#include<conio.h>3 R: \1 I, P$ a  V8 ?5 ^2 D2 c
#include<stdarg.h>
% }0 ^6 m5 ~6 F# v% nvoid tVarArg(int num,...);/*num为可变参数的个数*/
5 f4 g0 y3 ?. _3 Z4 c( K" H, lint main(void)0 b1 }+ k/ k; k& O" C3 d* t
{8 f$ z* ]8 n$ c/ O. q! g
clrscr();
% y3 e9 r  b6 D8 e& L# `; ~tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
) A* o# w  v2 S, w- j( `tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");* @$ L1 }. F+ j
getch();) u0 ]3 y+ H% k5 X
return 0;+ C# C( f+ ~# w0 ^8 _* f. [0 t
}" p3 E  m+ Q( [/ q
void tVarArg(int num,...)
& z: S0 L' x! T8 @5 t" |1 |7 T{5 I9 R9 Q! A: H' z" ?* |5 o! \5 e
va_list argp;  /*定义一个指向可变参数的变量*/
5 C. w. c; F6 Cva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/: ^" @: t- [7 [* ^8 q! @1 w
while(--num>=0)+ y  ?  A9 L4 w9 |4 c
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
1 w( X6 z$ Y1 O5 ?- \* v; B) E( f    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
1 j4 f5 M" ]% J* A1 ~* \0 fva_end(argp);  /*结束可变参数获取*/. b3 B* }3 L- H: g
return ;
' t: [4 T- r1 M}  u" b  _# G5 I; \

( V" C' C, Z/ ~* Q# q- h2 G4)可变参数的使用需要注意的问题
; `7 _$ ~; a" j+ z/ H
# }' g+ y. z3 A' b1.每个函数的可变参数至多有一个.2 Y5 B, p$ D9 O2 J  K1 z
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.1 ~" `" j2 o8 ^! `7 R9 O' S
3.可变参数的个数不确定,完全由程序约定.
  S+ ^; f  `1 o) o4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
6 k- L6 C# _# N而printf()中不是实现了识别参数吗?那是因为函数 & f& C2 n/ b- h2 O" w' ?5 w
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
" U3 P- X/ B) F% x- g! Q的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
( D& ^! X, V; n) o& g$ S9 f  r9 }* {) C! B过在自己的程序里作判断来实现的.
$ [4 m0 o8 b" J% h$ K8 h4 D5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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