返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.- p2 T: g5 g. E
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
' J' A" s" E# h( l. G可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不7 R" q7 ?) q5 W1 \7 ?) f7 e
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有6 Z: W8 o, @+ @  C- j
实际的名称与之相对应.
" x7 i& O7 E/ j% F! a6 L  C+ ]& P由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
, s$ o) q1 R  O4 D0 z3 z' G4 Z1 }然而,更多地自由,同样也加大操作上的难度.( [  h4 C* F$ p$ {: n
以下就对可变参数的几个方面作一定的介绍.
8 r3 T3 V+ _: [8 w' q- \: g) T/ M8 V6 S/ J3 j% a0 T
1)可变参数的存储形式.
# @) Z3 H! b& o- J# T! Q2 \: W4 y5 X; A2 B5 l- U. J
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
  b7 u  o) v) K- |存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区., y9 `# v1 G1 }' ^; F2 l
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
5 e- I. B, k2 N& v& h- w这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.3 x3 l/ `4 r$ T* g! Z$ a' L# F
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
$ @; @* m/ ^/ @- L3 W% f栈区:
- Y* K9 O  l3 i5 c( ~& }4 _# u
" T" w3 l8 l& y! Z|栈顶             低地址: V& _$ K2 j- p$ e- E1 P# a

' P( q; ^- B& t% N; q0 a; ||第一个固定参数var1
% Q' @- y$ C' \" H8 R|可变参数前的第一个固定参数var2: b2 }9 W# q* u( q( c. l
|可变参数的第一个参数
- d; h. a2 R; C* e" v|...
. F# [1 s3 d5 }! J1 \7 S|可变参数的最后一个参数$ ~% u2 Y. U" {1 b, v2 v# {4 c
|函数的倒数第二个固定参数var38 v: ~4 g$ F  r9 H% N1 l. t
|函数的最后一个固定参数var4. i. l4 F( f( Y
|...
4 y( M$ X$ C0 d4 N1 z1 r|函数的返回地址9 v7 q5 u  k, F: Z' k
|...
4 ?" A0 t( N4 \- c6 c; H|栈底    高地址: _6 y4 \2 J( q. V- A
, ]5 T  {% F5 h
2)使用可变参数所用到头文件和相关宏说明
! D3 X4 x' C7 K% I
0 X2 F  V- R+ K8 q: i在此,以TC2.0编译器为参考对象来说明.
: e0 R# ^- [2 F7 n6 f8 n8 V可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中./ f, U/ J' U8 ]2 l9 W
此文件为:* x: x6 x; G2 I: }3 \$ E
/* stdarg.h
- X1 x; q3 U  t
9 k, ], _4 _; @0 G. ~+ `Definitions for ACCESSing parameters in functions that accept
% ^. K& P8 Z; w( ua variable number of arguments./ D% ]) K5 E7 t- K  I! }+ T
# m  T* S2 }) s. ?& M. Q
Copyright (c) Borland International 1987,1988# o% B; Z4 W" L9 U5 O
All Rights Reserved.8 |" t' i0 K: |! E
*/
& R  |; a. Z4 o; x# n! R5 n! l#if __STDC__4 |8 r1 ^8 [  A' }  g
#define _Cdecl
5 {( {. ?$ S3 v6 h#else
) Y+ f# ^# ~! N2 W2 {  n+ c+ e#define _Cdecl cdecl) o5 y0 ]" t3 t7 V# ~
#endif
/ j+ E/ k2 p0 f6 L! ?9 }( Y0 H& H* s+ G- |/ _0 m- {* @
#if !defined(__STDARG)
. P6 @4 L7 j+ S( Q+ T4 e$ e#define __STDARG
5 x1 F& y' H: F. E9 ~; M' G; a$ O: D7 c
typedef void *va_list;( U: t& W3 t9 Y  z3 h

/ T( Z1 B6 M& b% B- g#define va_start(ap, parmN) (ap = ...)
0 C- B% @! X- u; d9 a#define va_arg(ap, type) (*((type *)(ap))++)2 x. f1 A' n8 H5 N* x9 R$ U
#define va_end(ap)* z( T6 X) `7 \& g8 K! C
#define _va_ptr   (...)
, S6 {5 R6 v9 b3 T) }  K9 G- I/ A+ b#endif
5 ]: r9 ?9 n# q+ r. W3 x+ |
9 N3 H! q% t5 b3 Q& `以上为"STDARG.H"的内容.' B& Q. Q6 r/ Z& q, h
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
  G, ]$ T, l" x+ a( Eva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
4 ]$ i- i+ I6 O, m) g  l6 A. i2 ~: QparmN为可变参数的前面一个固定参数.  g+ j3 t' b% U6 i
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.- r0 G" B1 L+ ~1 n' I
va_end(ap) 结束可变参数获取.: x: ^6 n2 y4 b8 m5 t" J
' T5 R5 @9 u8 V
3)可变参数的使用实例0 N% ~( T& {+ ^( {( x; Z
& ?" L5 \1 @4 L% `/ q* \
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
% M% G9 s, @2 Z
5 E- ~$ s3 y5 S! O#include<stdio.h>; q+ {! M) ~3 o( s. L
#include<conio.h>
7 ^$ m/ q9 H& s) {#include<stdarg.h>+ V8 y# ~! E2 N8 }$ d
void tVarArg(int num,...);/*num为可变参数的个数*/) f% a- m* Z4 |: I+ Y
int main(void), ]6 T" J0 c: N' w
{
: y) M% M# f3 @clrscr();
+ W0 {3 n/ \& R3 xtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");6 S( D" y/ I  _# R$ G
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");7 y  o% m: P9 D
getch();+ N" J1 c* I6 q
return 0;
9 d1 v/ ~2 a1 G& D2 G3 e3 `}4 l3 V+ _) c5 r2 i# d3 h: X; K% s" z
void tVarArg(int num,...): d' s7 h) B4 M3 l9 d
{
5 J" E% W! Q3 H. G/ Wva_list argp;  /*定义一个指向可变参数的变量*/
0 l7 `% I2 u. `  T3 Ava_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/! Z) L2 Y5 Q& i% K' N4 {6 Y- S" f
while(--num>=0)
7 m! m, z( Y4 @! h  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,: L& M8 r3 L4 a5 {  z3 I1 c/ \0 M7 i0 D
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/. Z$ ~4 r8 {: m. P/ b0 V
va_end(argp);  /*结束可变参数获取*/6 t( B+ h/ B9 i) z$ r9 m# ?' w2 H, N
return ;
& f7 J4 E% Z( u$ }& v}+ [) D% P! J' m4 P' S. L

1 a, ]; I& @& y7 D9 V5 Z- {4 J4)可变参数的使用需要注意的问题. D) g, t& w$ U( y3 S" f

+ i/ c2 \/ N  }5 F. A1.每个函数的可变参数至多有一个.
+ c+ E/ p* Z; j2 N9 E2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
; b: j  t7 M# N1 {8 \- w8 D0 V3 N5 {3 D3.可变参数的个数不确定,完全由程序约定.
3 |4 V, B, |) ^4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.& u$ w% w' R9 d$ o: N! s
而printf()中不是实现了识别参数吗?那是因为函数 4 ~( v- p! S9 t9 q
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg / ~  J  l/ _' Y1 \9 I' l
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
  f0 H/ j% J; B1 e9 X过在自己的程序里作判断来实现的.
8 E- n: m7 s0 ^5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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