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

函数的可变参数详谈

可变参数的英文表示为:variable argument.! g0 z* a& L5 G! A# w& [+ {1 ^
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.* |5 m) c5 F7 U1 a
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不$ G4 I4 F5 \6 b% D
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有, I# t6 @- W3 [# K6 c
实际的名称与之相对应.
4 |- y6 v( R% o9 h由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
' J; m) M8 k% m0 z- g3 K然而,更多地自由,同样也加大操作上的难度.
% i  o$ E; W% u* C4 h9 B以下就对可变参数的几个方面作一定的介绍.! Z/ ~5 ?2 d+ M* Q" G
; m. k) a) P& v
1)可变参数的存储形式.
. X. V% w- O$ w$ g1 f1 t* q9 K( _5 ?
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,- o& H0 K7 J& t  Z  X) c
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.1 C) k# m* v0 `" c* N
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
# N9 g6 P/ X9 [3 Z) Z这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.+ L9 k2 I. b1 H
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
( s7 W6 J- H: c. _+ D; U栈区:+ [* B1 O, @8 i* w8 B# x! }0 b# n/ w

2 V: k+ l5 Q* f3 t8 z0 C' v|栈顶             低地址" [+ q1 Y7 k* ?

3 H  V6 Y' Q. |6 m|第一个固定参数var18 p6 h1 u: t5 w* R$ Z7 N& f
|可变参数前的第一个固定参数var2) s% X# Z* R, a; K$ @" G
|可变参数的第一个参数
; ~, s8 F: G$ I! d9 x. b|...
, i, X  m7 R6 D/ d+ @' Y% n|可变参数的最后一个参数
, j3 L7 l, l: I|函数的倒数第二个固定参数var31 d2 |* _, k& p- ^0 a
|函数的最后一个固定参数var4
- T; s# G6 i  Y|...
0 e7 C& |! c7 t6 U- |$ Q( \|函数的返回地址3 V8 o4 I" ^) O! s
|.... h$ I# K* @+ U! H3 \9 `" Q
|栈底    高地址0 B" t; T" v& f
; l! U* i- \  z) x$ n4 l) w
2)使用可变参数所用到头文件和相关宏说明
# f3 v% F! E9 R! E) k' r( ~8 a" R* V: a
在此,以TC2.0编译器为参考对象来说明.
4 K0 @  i) J3 s- |: t; q" f可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
* n5 l: ~6 K. k, N9 p4 W此文件为:) w, c5 a/ H0 T7 @5 o/ Z7 F
/* stdarg.h1 E! \( q, O3 b' c
- c$ j" @! E5 Q  y" J
Definitions for ACCESSing parameters in functions that accept
  ]# O: e  n: q0 d& Qa variable number of arguments.
! q* B$ \3 C; ?: G$ L5 m9 ^" V; h, @2 k6 m) g: ]6 M  t
Copyright (c) Borland International 1987,1988* E/ U% {1 l& I2 |8 z! U3 Z3 ?- T+ Z
All Rights Reserved.
( Y$ a1 d& C7 R: h9 @*/
! Y4 v2 h" B+ |% i#if __STDC__
" f2 G. Z3 j  }% \( L+ ?  n; {#define _Cdecl
: Z) L1 N2 r4 j) _#else3 V' j9 Y4 O$ J
#define _Cdecl cdecl; r$ a% q0 E5 @( l9 s
#endif
& m# T4 o* j$ q3 W( ^' M: C( h3 @7 u8 c0 v
#if !defined(__STDARG)1 R$ ^7 r+ f% a1 i$ C
#define __STDARG$ V' \$ B8 R' }  W6 y% ?

; O0 P6 T# h' w3 V2 w! ptypedef void *va_list;' v' n" b, j7 L# P7 D
; R+ \6 @- B, `6 V! x& ^# H
#define va_start(ap, parmN) (ap = ...)2 g  _$ S$ Q8 C0 K
#define va_arg(ap, type) (*((type *)(ap))++)! X/ {- [# k' ?) u
#define va_end(ap)
. m! s$ e: t8 e7 y#define _va_ptr   (...)
$ i8 {% `9 A& r8 w: _" B#endif; N" T) d. p; M9 R
( l- F( K  \6 f$ S
以上为"STDARG.H"的内容.
- J9 m+ S+ G  m5 a. ]该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;' u2 L/ Y( O! G7 e% G6 C2 @$ N
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
. L3 q& t9 N" o% q9 Y' |parmN为可变参数的前面一个固定参数.0 F6 d6 z6 M! G0 Z7 N
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.. X% m6 n" U6 T* x4 h5 {
va_end(ap) 结束可变参数获取.
& _& \- a9 g$ @5 e' Q9 V* @* u+ @% ]$ t
3)可变参数的使用实例
7 b- G9 k5 j/ x: a( y- ~4 c, u; _9 ]  n% i+ ?( g
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
' p/ w" }6 k4 X9 P& z9 _! T
' f% }' H# {- j* m. Z#include<stdio.h>4 R( \$ c: t; I- U
#include<conio.h>: B7 w3 b9 m9 i
#include<stdarg.h>0 c7 s$ X1 l4 g% H$ W: t
void tVarArg(int num,...);/*num为可变参数的个数*/7 I9 Z% d  g0 V
int main(void)
/ J3 \" B4 v1 ]% u: m{
8 b2 G' n" }# ^. n/ o5 D; }5 @$ ~clrscr();
1 ~6 o& E1 H- p3 d9 {1 @6 ctVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
( G1 O" \5 z5 m5 StVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
9 W5 ]4 x; @$ `" g- M2 f3 @getch();
5 g( {# c! p$ Q, R* W# f, ?return 0;9 D* G. j/ S1 c+ b* N0 S2 O, N
}2 Y3 `5 i5 C' b
void tVarArg(int num,...)5 Z9 e' G) F2 y, B1 e7 o
{- C2 J- D$ F  D5 k- n! h
va_list argp;  /*定义一个指向可变参数的变量*/% u3 S& r7 q* i% j7 @
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
3 k% h9 [" A5 l6 t& B2 Hwhile(--num>=0)) A, f( [5 g( H1 p! a8 |
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
5 {/ |. F3 ?5 ~& v- d' n    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
% N7 J* n5 @" n! tva_end(argp);  /*结束可变参数获取*// J6 @/ m3 l1 Y9 u0 `! B1 s
return ;
9 w" j& W/ ?: z, s}& V: _, ^7 z" M3 a
' v1 d2 K% ^( I  c
4)可变参数的使用需要注意的问题
- _* W0 ^; e* F4 e$ m& f' Q1 Q, E( @' F0 _, d# p* m, v  M& E
1.每个函数的可变参数至多有一个.
7 J  V5 A0 S5 s9 J0 c4 e0 l2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.$ V% Y3 q( ~2 A( l
3.可变参数的个数不确定,完全由程序约定.
3 V1 V* I& j- |8 b0 s4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.' M1 j, _% c5 p5 s, Q" i: `' @$ b
而printf()中不是实现了识别参数吗?那是因为函数
. N8 T2 o; G, G7 i" eprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
* C! N$ u2 _3 e4 T3 S的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
: k4 ]- d  g1 d1 E% }- S过在自己的程序里作判断来实现的.
/ k9 o5 F& b8 M' m: c* }5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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