# D3 p3 N1 a6 Y1 l, t1 }4 i/ C1)可变参数的存储形式.. K7 U i# r5 g. X5 P
% f6 [3 U2 a, G- B( W0 t# X
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,2 ?- [" m# e9 p0 A# ~3 F9 |8 V
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区. 8 c& x" ` w% g, U8 P- g在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈, $ l- I0 [9 D9 c H9 ^) c这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存. 5 `& `6 v* J; t) {因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例): ( a8 a$ u" X6 G' Q1 m栈区: y4 ~ t, u( m: `. C
5 ]( m, P3 `0 {: P$ P o
|栈顶 低地址' N) w8 f. a8 U- r3 c d' O/ N
. v$ p; w" ~8 L9 W
|第一个固定参数var1 9 F% `. J1 h; O2 ^. ]8 G* ^1 i6 E|可变参数前的第一个固定参数var28 t# n8 @1 i2 S }" `) N
|可变参数的第一个参数 9 r1 ]: O: ?# y9 q6 o; D|...4 ?; u; I# [( j6 X6 [5 x: ^/ ^
|可变参数的最后一个参数" }8 p% T5 M+ ]/ \
|函数的倒数第二个固定参数var3 ; u3 J( J! F" E& C8 i5 W$ `7 s|函数的最后一个固定参数var4 / }5 g8 }1 D! W, [* S|...7 X7 b. y( t2 v$ t# X) W
|函数的返回地址 I. Z; S ~1 V. b) z3 f! L/ I|...5 }8 J1 b# W9 q# _' p! ~% o
|栈底 高地址/ a3 L' Z/ W8 R! ^+ F& @
% u5 C% V) I1 U$ C
2)使用可变参数所用到头文件和相关宏说明! H# }- G! ^9 n# b8 [' G
; V+ ^0 i8 h, z6 y, h
在此,以TC2.0编译器为参考对象来说明.; H& C, s* x4 a7 v* Y4 |. m# d/ e7 Q' X
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中. $ i# W0 Y1 y: f2 R4 G$ d- J4 i此文件为: 8 [8 Y3 p8 t% P! ?/* stdarg.h" [4 D/ J/ B C% T
9 a4 h' f1 A4 ?2 o+ vDefinitions for ACCESSing parameters in functions that accept5 U6 T, D7 L; t( o9 Z! l7 m: X/ @% y
a variable number of arguments.8 x1 Z1 u3 x/ ^& A) n4 U
& ?2 L3 T6 D( l+ K UCopyright (c) Borland International 1987,1988( D& K3 n4 |7 s
All Rights Reserved./ k' p6 n5 F! i1 A) ]
*/, b) h) \: c7 h
#if __STDC__3 J9 n$ }* I, i W- M
#define _Cdecl6 }# R- L3 {! _- T3 Z! _
#else + b( g% T+ ]3 D' K/ @#define _Cdecl cdecl ! a1 w" K# E. M#endif # x. z0 I; i$ @* K, u! v9 h , L5 K9 u2 A0 F/ N- x1 H#if !defined(__STDARG)/ z% V) \- n; w+ U6 ?
#define __STDARG! X0 y7 I z9 @# v* g4 k. z
: Y& p, e: H& ntypedef void *va_list;1 S2 F1 @: l9 p. e7 G2 k. K
& T3 o, b5 U7 G% \#define va_start(ap, parmN) (ap = ...)) n D! ^+ k8 @
#define va_arg(ap, type) (*((type *)(ap))++)" Z( W- Y4 K: |* j- R
#define va_end(ap) 7 i( y9 E) U0 ^$ g) h#define _va_ptr (...)- \3 Y5 x3 `3 P1 o
#endif ! K8 } b7 B) p& c& N" S; V8 U+ F8 }' v2 \
以上为"STDARG.H"的内容.2 ?+ V4 C& ~8 H0 G
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list; q# P5 E, @; a# z. ]1 |- m
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list, s. O( J/ u. s
parmN为可变参数的前面一个固定参数.: @2 l4 w& {. V9 P8 F# @/ e
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型. / g$ c& V& w* j+ \/ dva_end(ap) 结束可变参数获取. ! j: U! W& ~! _; J0 H( ]* N 3 e7 V0 C( O) [6 O; w3)可变参数的使用实例- [$ f2 Y% U- m1 ?* B" g/ X
- n2 B- g2 D2 G/ T* c" ?" S4 v: x% w0 C
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串. 6 N, {8 ~9 r$ O" o% i : |: l! Q/ k( U. V#include<stdio.h>8 N X6 l0 _0 j, e, O
#include<conio.h>. i5 A: \9 b$ w T; ?8 o% `! R6 L' l
#include<stdarg.h>8 W: S2 G; g. \5 B( T
void tVarArg(int num,...);/*num为可变参数的个数*/ * m8 z; Y% u8 S1 C3 }int main(void)5 Y/ ^1 h R- K3 W# ]
{% u8 R1 V' I. P! `
clrscr(); % n% F* X" ]' x3 KtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n"); ; t/ N, Z# j8 AtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");0 r; r4 \3 `. j+ l+ ]' Q
getch(); 3 I. _- l3 Z. A6 ~return 0;1 ?, y! E1 y9 k
} 1 N' J- K6 j. h+ X- @% D( Mvoid tVarArg(int num,...) y8 E% O6 y( @0 Z; @" c6 n* |{ Q# z- j8 h5 i$ H' ^4 E
va_list argp; /*定义一个指向可变参数的变量*/$ U/ O3 _' i. @" K1 e9 I7 O
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/ % X d" }& ~9 `' Z4 {5 A8 e) Awhile(--num>=0)+ ~! ?( {( j; y3 a% [+ ] U
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数, 9 ?+ z; b1 D" ?, J$ n 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/ , ^- q8 @/ U6 R4 |, L$ M& |4 j6 tva_end(argp); /*结束可变参数获取*/ , F) ?/ }( m1 i4 {. j) ?# g2 T+ Ireturn ;* {5 A+ n+ B$ ]$ }+ G: T
}: J$ k+ {: o5 B, h
- Q2 r: p) D" D, x
4)可变参数的使用需要注意的问题1 ?8 S( Y7 a% F/ A; o( q0 C3 |
& D g4 Z: y; l: ~1.每个函数的可变参数至多有一个. 8 }: T2 y# W Z- A" L2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数. ; j/ e7 y+ [( ]! {2 q3.可变参数的个数不确定,完全由程序约定.% I9 d: Q; P( P. B4 x9 z
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换. 0 k9 \! [ l; ]而printf()中不是实现了识别参数吗?那是因为函数 , l, |: |' u# Y8 r& Jprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg ! |& x! `. @5 y2 \3 u3 Y/ }的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 6 s/ ^/ a. R1 f8 b) E2 c4 y0 Q
过在自己的程序里作判断来实现的. ) Y1 v. M& Q- w
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.