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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
* e  S3 M; z0 z$ E8 B. l它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.2 y" J' ?$ o/ K. C* t; A
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
7 b  q0 O7 e  o定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
6 t5 F: ~+ q, g6 l实际的名称与之相对应.
' o6 X: R# Z0 J: E0 o由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
! p' p- }* C  _然而,更多地自由,同样也加大操作上的难度.1 D0 M6 {# b8 F( |7 V
以下就对可变参数的几个方面作一定的介绍.
* g% n! @2 C. B# t( r/ ?, b) a9 C: |1 ^0 u1 D
1)可变参数的存储形式.
3 |- N& N( s$ @. A, d1 Q3 e; |6 o% @7 F6 i
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,& f2 {" D0 d3 s* A7 ^! e( Q% }
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
0 V) P& |/ h, K& o在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,: n. y4 c  v- x  m8 u+ g
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.. }) d8 j6 S; e; l1 D5 J0 j8 y
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
: Z7 @- U3 D% e% }栈区:/ u3 k1 ]; i+ z# D: X6 X& W; K

; o* q# M$ M0 d6 o7 w& P. a|栈顶             低地址0 l8 P0 J4 h4 R5 Z0 E4 U
) n3 C5 `' M0 a  l. s
|第一个固定参数var1" s8 g8 d, g5 }9 A, ]' h
|可变参数前的第一个固定参数var2! |- N0 s5 v- i8 H8 j9 ?& F
|可变参数的第一个参数
% z6 _9 x7 Z- t; N0 X# w|...
3 N3 {* c# M) i& y|可变参数的最后一个参数
0 r7 e+ @0 n5 X) s# c  V|函数的倒数第二个固定参数var3
' z& r0 N4 r; {* f|函数的最后一个固定参数var4
" Q; D1 G; s& i|...
1 z; I. x/ b; D8 p* N, S+ l7 q|函数的返回地址
9 t9 g: S6 S7 h: Y3 a! V% L|...
% @1 z( I5 J# P, W, H, h0 \  t# a|栈底    高地址
3 [- t# V3 U2 ^, N5 }+ B0 ^: r7 d3 K" X) `, D
2)使用可变参数所用到头文件和相关宏说明* `# B# e* t0 S6 v

* n6 d: L4 O7 I6 ?在此,以TC2.0编译器为参考对象来说明.
8 X9 h* ?9 w" W! D! {可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
* f* `# Z0 G# G& ^% l; r/ B) l! ^, n7 i此文件为:5 J+ e2 P& L' T- H
/* stdarg.h
0 Z3 [$ J; X# S5 e6 p' j! P& O0 s9 L1 A) {1 z5 Y
Definitions for ACCESSing parameters in functions that accept4 ]6 `0 v# \$ z1 E" L
a variable number of arguments.
% [, {, Z  `9 c3 t) S
7 o4 e" Q, v$ k8 Q6 o2 cCopyright (c) Borland International 1987,19881 V# F3 N+ B; }& C
All Rights Reserved.) Q" P6 i. n; [/ f& z6 m
*/: N, ^. t1 g4 m0 K
#if __STDC__
& U( X8 w% W+ d6 W#define _Cdecl' e' ^7 [) p, M9 V$ i
#else1 ~* ~2 l3 o3 n( H
#define _Cdecl cdecl1 R4 p( C4 g& y% }9 k
#endif
% @8 L/ l2 C; a; k) f, {
" N. M2 i; K3 l) ]1 {) A) }) G#if !defined(__STDARG)
' Z, y  t: e  a" V' M$ ?, f, z#define __STDARG
2 N+ @$ Y6 p3 ]* q
* b# ^9 v5 \- r) Vtypedef void *va_list;3 J9 W$ O1 n' z

6 V/ c( t3 R5 @7 b6 K#define va_start(ap, parmN) (ap = ...)1 D5 A; M% \0 e( }/ h8 D9 I
#define va_arg(ap, type) (*((type *)(ap))++)! w8 {5 t# Z4 @: ?3 N
#define va_end(ap). K. N0 h5 a1 P* \) {* A
#define _va_ptr   (...)
; _# }$ a' F9 @. Q. ^* ^& E#endif: }9 B" B- S6 m( x3 \
2 Z# K# `+ ~+ K* z
以上为"STDARG.H"的内容.
* e; j' f7 n5 A2 s$ Y) F! v9 X该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
& h8 C* k* N! k0 s0 K# H" mva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
" W" }! s. `% t" q. ^; M4 L9 m, gparmN为可变参数的前面一个固定参数.& w9 }( Y# m/ [' _0 x
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.& z4 S8 V0 i: i1 t+ v* r
va_end(ap) 结束可变参数获取.3 A8 P4 w/ R# g' y6 a  P5 w4 g

9 v# {9 [5 z/ n# K) B3)可变参数的使用实例
+ i9 F5 O) u) Y1 B) S0 i
, x# U" k. I3 p" Y  j4 S4 p实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
# h2 |  w9 _$ B+ M5 E& _
( t$ Y/ d- X1 I9 Y# [! j  u#include<stdio.h>3 Q3 j; I8 y! a- y$ o2 T
#include<conio.h>4 L" R: R: U  `8 W' T. T. Q
#include<stdarg.h>3 b, Z2 v' o0 Q/ v9 X! v7 B
void tVarArg(int num,...);/*num为可变参数的个数*/9 F( D, Q+ V, Q( B$ V8 y! I% D
int main(void)9 B! ?9 H: c: Z% h7 D
{
* j- a1 ?. `4 f+ B" n8 j& s" Yclrscr();
+ C2 u4 C! [9 p( d. O9 HtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");4 R2 u* u" X: Y% c2 ~! w
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");+ \. d' J. |0 a
getch();
5 h+ |! S6 H0 ]- Oreturn 0;
; ^. T3 `2 K5 _# |( U}4 G9 }6 f: ]6 H* n
void tVarArg(int num,...)
' U+ l% a; [) V  x: {) [0 _  h{
) ^( [  _$ f' r# C7 [% Jva_list argp;  /*定义一个指向可变参数的变量*/
# k( t& z# ?2 j4 N  T7 \3 bva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/4 g1 f, J- X, _, k7 N) G
while(--num>=0)+ B, g+ q4 A3 k  w7 J
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,# w" g! t2 d8 ?$ p
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
  e7 S! n/ ~0 S2 Vva_end(argp);  /*结束可变参数获取*/3 \( C2 C7 d" \$ C% z
return ;, L* @. Q/ Q5 a3 A( K
}
0 r' H3 T# q$ J+ _9 Z* V! `4 j) ^3 j3 |7 p- B( t) |
4)可变参数的使用需要注意的问题
* h! d+ k2 ~& C7 c2 [: t
4 i4 M$ n: Q9 ~3 R6 _9 {( {! Z1.每个函数的可变参数至多有一个.2 ]! d& t% k# j4 r, h' Q
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.% J' k/ `% S6 `" s2 X9 @* W/ f$ T
3.可变参数的个数不确定,完全由程序约定.
% A: l" e; |( P1 d: O4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
* \1 E$ d8 @9 O( \而printf()中不是实现了识别参数吗?那是因为函数
% u) y5 |$ Q9 J( b  {- ?printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 1 L, I, f4 V2 Y) A3 k* ?% U( Z$ H
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 : T$ R$ Y7 r! B% `1 M7 [! C! @1 X# [
过在自己的程序里作判断来实现的.   N' y2 ^% ^& X' y
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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