vastartvaendvalist的使用.docx

上传人:b****5 文档编号:7471688 上传时间:2023-01-24 格式:DOCX 页数:23 大小:31.80KB
下载 相关 举报
vastartvaendvalist的使用.docx_第1页
第1页 / 共23页
vastartvaendvalist的使用.docx_第2页
第2页 / 共23页
vastartvaendvalist的使用.docx_第3页
第3页 / 共23页
vastartvaendvalist的使用.docx_第4页
第4页 / 共23页
vastartvaendvalist的使用.docx_第5页
第5页 / 共23页
点击查看更多>>
下载资源
资源描述

vastartvaendvalist的使用.docx

《vastartvaendvalist的使用.docx》由会员分享,可在线阅读,更多相关《vastartvaendvalist的使用.docx(23页珍藏版)》请在冰豆网上搜索。

vastartvaendvalist的使用.docx

vastartvaendvalist的使用

va_start、va_end、va_list的使用

1:

当无法列出传递函数的所有实参的类型和数目时,可用省略号指定参数表

voidfoo(...);

void

foo(parm_list,...);

 

2:

函数参数的传递原理

函数参数是以数据结构:

栈的形式存取,从右至左入栈.eg:

#include

voidfun(inta,...)

{

int*temp=

&a;

temp++;

for(inti=0;i

{

cout<<*temp

<

temp++;

}

}

 

intmain()

{

inta=1;

intb=2;

intc=3;

intd=

4;

fun(4,a,b,c,d);

system("pause");

return

0;

}

Output:

:

1

2

3

4

 

3:

获取省略号指定的参数

在函数体中声明一个va_list,然后用va_start函数来获取参数列表中的参数,使用完毕后调用va_end()结束。

像这段代码:

void

TestFun(char*pszDest,intDestLen,constchar*pszFormat,...)

{

va_list

args;

va_start(args,

pszFormat);

_vsnprintf(pszDest,DestLen,pszFormat,

args);

va_end(args);

}

 

4.va_start使argp指向第一个可选参数。

va_arg返回参数列表中的当前参数并使argp指向参数列表中的下一个参数。

va_end把argp指针清为NULL。

函数体内可以多次遍历这些参数,但是都必须以va_start开始,并以va_end结尾。

 

  1).演示如何使用参数个数可变的函数,采用ANSI标准形式

  #include〈stdio.h〉

  #include

〈string.h〉

  #include〈stdarg.h〉

  

  intdemo(char,...);

  void

main(void)

  {

    demo("DEMO","This","is","a","demo!

",

"");

  }

  

  intdemo(charmsg,...

  {

      

    va_list

argp;

    intargno=0;

    charpara;

 

    

    va_start(argp,msg

);

    while

(1)

      

{

     para=va_arg(argp,

char);

        if(strcmp(para,"")==0

      

break;

        printf("Parameter#%dis:

%s\n",argno,para);

        argno++;

  

}

  va_end(argp);

  

   return0;

  }

 

2)//示例代码1:

可变参数函数的使用

#include"stdio.h"

#include"stdarg.h"

void

simple_va_fun(intstart,...)

{

   va_list

arg_ptr;

  intnArgValue=start;

   int

nArgCout=0;    //可变参数的数目

   va_start(arg_ptr,start);

//以固定参数的地址为起点确定变参的内存起始地址。

   do

   

{

       

++nArgCout;

       printf("the%dtharg:

%d\n",nArgCout,nArgValue);    

//输出各参数的值

       nArgValue=

va_arg(arg_ptr,int);                     

//得到下一个可变参数的值

   }while(nArgValue!

=

-1);               

   

return;

}

intmain(intargc,char*argv[])

{

   

simple_va_fun(100,-1);

   

simple_va_fun(100,200,-1);

   return0;

}

 

3)//示例代码2:

扩展——自己实现简单的可变参数的函数。

下面是一个简单的printf函数的实现,参考了

Language>中的例子

#include"stdio.h"

#include"stdlib.h"

void

myprintf(char*fmt,...)       

//一个简单的类似于printf的实现,//参数必须都是int类型

{

   char*

pArg=NULL;              

//等价于原来的va_list

   char

c;

   

   pArg=(char*)

&fmt;         //注意不要写成p=fmt

!

!

因为这里要对//参数取址,而不是取值

   pArg+=

sizeof(fmt);        //等价于原来的va_start         

   

do

   {

       c

=*fmt;

       if(c!

=

'%')

       

{

           

putchar(c);           

//照原样输出字符

       

}

       

else

       

{

          

//按格式字符输出数据

          

switch(*++fmt)

          

{

           

case'd':

               

printf("%d",*((int*)pArg));          

               

break;

           

case'x':

               

printf("%#x",*((int*)pArg));

               

break;

           

default:

               

break;

           

}

           pArg+=

sizeof(int);              

//等价于原来的va_arg

       

}

       ++fmt;

   

}while(*fmt!

='\0');

   pArg=

NULL;                              

//等价于va_end

   return;

}

intmain(intargc,char*

argv[])

{

   inti=1234;

   intj=

5678;

   

   myprintf("thefirst

test:

i=%d\n",i,j);

   myprintf("thesecendtest:

i=%d;

%x;j=%d;\n",i,0xabcd,j);

   

system("pause");

   return0;

}

在标准文件stdarg.h中包含带参数的宏定义:

C代码

 

1.typedef __builtin_va_list __gnuc_va_list;  

2.typedef __gnuc_va_list va_list   

3.#define va_start(v,l)   __builtin_va_start(v,l)  

4.#define va_end(v)       __builtin_va_end(v)  

5.#define va_arg(v,l)     __builtin_va_arg(v,l)  

typedef__builtin_va_list__gnuc_va_list;

typedef__gnuc_va_listva_list

#defineva_start(v,l)__builtin_va_start(v,l)

#defineva_end(v)__builtin_va_end(v)

#defineva_arg(v,l)__builtin_va_arg(v,l)

 __builtin_va_list等带有__builtin都是编译器所能识别的函数,类似于关键字,这里就不深究了。

 

 

下面举例一下可变参数的使用注意事项:

1,可变长参数函数规定格式{类型函数名(firstfix,…,lastfix,…)}。

firstfix,…,lastfix表示函数参数列表中的第一个和最后一个固定参数,该参数列表至少要有一个固定参数,其作用是为了给变参函数确定列表中参数的个数和参数的类型。

 

2,指针类型va_list用来说明一个变量ap(argumentpointer--可变参数指针),此变量将依次引用可变参数列表中用省略号“...”代替的每一个参数。

即指向将要操作的变参。

 

3,宏va_start(ap,lastfix)是为了初始化变参指针ap,以指向可变参数列表中未命名的第一个参数,即指向lastfix后的第一个变参。

它必须在指针使用之前调用一次该宏,参数列表中至少有一个未命名的可变参数。

从宏定义可知其正确性。

 

4,宏va_arg(ap,type)调用,将ap指向下一个可变参数,而ap的类型由type确定,type数据类型不使用float类型。

调用后将新的变参指向一个工作变参,如iap=va_start(ap,int)调用。

 

5,宏va_end(ap)功能是完成清除变量ap的作用,表明程序以后不再使用,若该指针变量需再使用,必须重新调用宏va_start以启动该变量。

 

下面举两个列子,一个使用可变参数实现,一个使用常规方式实现:

C代码

 

1.#include   

2.#include   

3.  

4.int add_va(int num, int first, ...) {  

5.        int ret = 0, arg, i;  

6.        va_list ap;  //定义一个可变参数变量  

7.  

8.        ret = first;  

9.        va_start(ap,first); //初始化可变参数  

10.        for(i = 1; i < num;i++) {  

11.                arg = va_arg(ap,int);//获取下一个类型为int的参数  

12.                ret += arg;  

13.        }  

14.  

15.        va_end(ap);//清除变量ap  

16.        return ret;  

17.}  

18.  

19.int add_normal(int num, ...) {  

20.        int *p, i;  

21.        int ret;  

22.        p = &num + 1; //p指向参数列表下一个位置  

23.        ret = *p;  

24.  

25.        for(i = 1; i < num; i++)  

26.                ret +=p[i];  

27.  

28.        return ret;  

29.}  

30.  

31.int main(int argc, char **argv) {  

32.        printf("%d\n", add_va(5,1,2,3,4,5));  

33.        printf("%d\n", add_normal(5,1,2,3,4,5));  

34.        return 0;  

35.}  

#include

#include

intadd_va(intnum,intfirst,...){

intret=0,arg,i;

va_listap;//定义一个可变参数变量

ret=first;

va_start(ap,first);//初始化可变参数

for(i=1;i

arg=va_arg(ap,int);//获取下一个类型为int的参数

ret+=arg;

}

va_end(ap);//清除变量ap

returnret;

}

intadd_normal(intnum,...){

int*p,i;

intret;

p=&num+1;//p指向参数列表下一个位置

ret=*p;

for(i=1;i

ret+=p[i];

returnret;

}

intmain(intargc,char**argv){

printf("%d\n",add_va(5,1,2,3,4,5));

printf("%d\n",add_normal(5,1,2,3,4,5));

return0;

}

 

得出结论:

15

15

 

可变参数在编译器中的处理

va_start,va_arg,va_end是在stdarg.h中被定义成宏的,由于1)硬件平台的不同2)编译器的不同,所以定义的宏也有所不同,下面以VC++中stdarg.h里x86平台的宏定义摘录如下:

C代码

 

1.typedef char * va_list;   

2.#define _INTSIZEOF(n) \   

3.((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )   

4.#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )   

5.#define va_arg(ap,t) \   

6.( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )   

7.#define va_end(ap) ( ap = (va_list)0 )   

typedefchar*va_list;

#define_INTSIZEOF(n)\

((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1))

#defineva_start(ap,v)(ap=(va_list)&v+_INTSIZEOF(v))

#defineva_arg(ap,t)\

(*(t*)((ap+=_INTSIZEOF(t))-_INTSIZEOF(t)))

#defineva_end(ap)(ap=(va_list)0)

 

_INTSIZEOF(n)中注意((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) ),其实这段表达式主要是为了字节对齐作用,这里是使用int值来确定对齐的方式。

很好很强大。

 

(1)是函数的参数在堆栈中的分布位置。

运行va_start(ap,v)以后,ap指向第一个可变参数在堆栈的地址

Java代码

 

1.高地址|-----------------------------|   

2.|函数返回地址 |   

3.|-----------------------------|   

4.|. |   

5.|-----------------------------|   

6.|第n个参数(第一个可变参数) |   

7.|-----------------------------|<--va_start后ap指向   

8.|第n-1个参数(最后一个固定参数)|   

9.低地址|-----------------------------|<-- &v   

10.图( 1 )   

高地址|-----------------------------|

|函数返回地址|

|-----------------------------|

|.|

|-----------------------------|

|第n个参数(第一个可变参数)|

|-----------------------------|<--va_start后ap指向

|第n-1个参数(最后一个固定参数)|

低地址|-----------------------------|<--&v

(1)

 

然后,使用va_arg()取得类型t的可变参数值。

举例va_arg取int型的返回值:

j=(*(int*)((ap+=_INTSIZEOF(int))-_INTSIZEOF(int)))。

1,ap+=sizeof(int),ap指向了下一个参数的地址

2,减去sizeof(int),相当于又返回到了前一个参数,不过这一步没有赋值给ap。

3, 将表达式的值强转成int类型。

4,返回第n(第一个可变参数)的值,并且赋值给变量j。

 

C代码

 

1.高地址|-----------------------------|   

2.|函数返回地址 |   

3.|-----------------------------|   

4.|. |   

5.|-----------------------------|<--va_arg后ap指向   

6.|第n个参数(第一个可变参数) |   

7.|-----------------------------|<--va_start后ap指向   

8.|第n-1个参数(最后一个固定参数)|   

9.低地址|-----------------------------|<-- &v   

10.图( 2 )   

高地址|-----------------------------|

|函数返回地址|

|-----------------------------|

|.|

|-----------------------------|<--va_arg后ap指向

|第n个参数(第一个可变参数)|

|-----------------------------|<--va_start后ap指向

|第n-1个参数(最后一个固定参数)|

低地址|-----------------------------|<--&v

(2)

 最后要说的是va_end宏的意思,x86平台定义为ap=(char*)0;使ap不再指向堆栈,而是跟NULL一样.有些直接定义为((void*)0),这样编译器不会为va_end产生代码,例如gcc在linux的x86平台就是这样定义的.

 

NOTE:

由于参数的地址用于va_start宏,所以参数不能声明为寄存器变量或作为函数或数组类型。

不同的操作系统和硬件平台的定义有些不同,但原理却是相似的。

 

 

接下来自己写了一个printf:

C代码

 

1.#include   

2.#include   

3.#include   

4.  

5.void printf_diy(char *fmt,...) {  

6.        va_list arg;  

7.        char c;  

8.  

9.        va_start(arg, fmt);  

10.  

11.        do {  

12.                c = *fmt;  

13.                if(c !

= '%'){  

14.                        putchar(c); //输出  

15.                }  

16.                else {  

17.                        fmt++;  

18.                        switch(*fmt) {  

19.                        case 'd':

  

20.                                printf("%d", *((int*)arg));  

21.                                break;  

22.                        case 'x':

  

23.                                printf("%#x", *((int*)arg));  

24.                                break;  

25.                        case 'f':

  

26.                                printf("%f", *((float*)arg));  

27.             

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 农林牧渔 > 林学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1