快捷搜索:  as  test  1111  test aNd 8=8  test++aNd+8=8  as++aNd+8=8  as aNd 8=8

和记娱最好h88285愽娱:cc++支持可变参数的函数



一、为什么要应用可变参数的函数?

一样平常我们编程的时刻,函数中形式参数的数目平日是确定的,在调用时要依次给出与形式参数对应的所有实际参数。但在某些环境下盼望函数的参数个数可以根据必要确定,是以c说话引入可变参数函数。这也是c功能强大年夜的一个方面,其它某些说话,比如fortran就没有这个功能。

范例的可变参数函数的例子有大年夜家认识的printf()、scanf()等。

二、c/c++若何实现可变参数的函数?

为了支持可变参数函数,C说话引入新的调用协议, 即C说话调用约定 __cdecl . 采纳C/C++说话编程的时刻,默认应用这个调用约定。假如要采纳其它调用约定,必须添加其它关键字声明,例如WIN32 API应用PASCAL调用约定,函数名字之前必须加__stdcall关键字。

采纳C调用约准时,函数的参数是从右到左入栈,个数可变。因为函数体不能预先知道传进来的参数个数,是以采纳本约准时必须由函数调用者认真客栈清理。举个例子:

//C调用约定函数

int __cdecl Add(int a, int b)

{

return (a + b);

}

函数调用:

Add(1, 2);

//汇编代码是:

push        2            ;参数b入栈

push        1   和记娱最好h88285愽娱         ;参数a入栈

call        @Add         ;调用函数。着实还有编译器用于定位函数的表达式这里把它省略了

add     和记娱最好h88285愽娱    esp,8        ;调用者认真和记娱最好h88285愽娱清栈

假如调用函数的时刻应用的调用协讲和函数原型中声明的不同等,就会导致栈差错,这是别的一个话题,这里不再细说。

别的c/c++编译器采纳宏的形式支持可变参数函数。这些宏包括va_start、va_arg和va_end等。之以是这么做,是为了增添法度榜样的可移植性。樊篱不合的硬件平台造成的差异。

支持可变参数函数的所有宏都定义在stdarg.h 和 varargs.h中。例如标准ANSI形式下,这些宏的定义是:

typedef char * va_list; //字符串指针

#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

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

#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

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

应用宏_INTSIZEOF是为了按照整数字节对齐指针,由于c调用协议下面,参数入栈都是整数字节(指针或者值)。

三、若何定义这类的函数。

可变参数函数在不合的系统下,采纳不合的形式定义。

1、用ANSI标准形式时,参数个数可变的函数的原型声明是:

type funcname(type para1, type para2, ……);

关于这个定义,有三点必要阐明:

一样平常来说,这种形式至少必要一个通俗的形式参数,可变参数便是经由过程三个'.'来定义的。以是"……"不表示省略,而是函数原型的一部分。type是函数返回值和形式参数的类型。

例如:

int MyPrintf(char const* fmt, ……);

然则,我们也可以这样定义函数:

void MyFunc(……);

然则,这样的话,我们就无法应用函数的参数了,由于无法经由过程上面所讲的宏来提取每个参数。以是除非你的函数代码中切实着实没有用到参数表中的任何参数,否则必须在参数表中应用至少一个通俗参数。

五、关于可变参数的通报问题

有人问到这个问题,要是我定义了一个可变参数函数,在这个函数内部又要调用其它可变参数函数,那么若何通报参数呢?上面的例子都是应用宏va_arg逐个把参数提掏出来应用,能否不提取,直接把它们通报给别的的函数呢?

我们先看printf的实现:

int __cdecl printf (const char *format, ...)

{

va_list arglist;

int buffing;

int retval;

va_start(arglist, format); //arglist指向format后面的第一个参数

...//不关心其它代码

retval = _output(stdout,format,arglist); //把format款式和参数通报给output函数

...//不关心其它代码

return(retval);

}

我们先仿照这个函数写一个:

#i nclude

#i nclude

int mywrite(char *fmt, ...)

{

va_list arglist;

va_start(arglist, fmt);

return printf(fmt,arglist);

}

void main()

{

int i=10, j=20;

char buf[] = 和记娱最好h88285愽娱"This is a test";

double f= 12.345;

mywrite("String: %s\nInt: %d, %d\nFloat :%4.2f\n", buf, i, j, f);

}

运行一下看看,哈,差错百出。仔细阐发缘故原由,根据宏的定义我们知道 arglis和记娱最好h88285愽娱t是一个指针,它指向第一个可变的参数,然则所有的参数都位于栈中,以是arglist指向栈中某个位置,经由过程arglist的值,我们可以直接查看栈里面的内容:

#i nclude

#i nclude

#i nclude

//测试一下这个,开个玩笑

void t(...)

{

printf("\n");

}

int mywrite(char *fmt, ...)

{

va_list arglist;

va_start(arglist, fmt);

char temp[255];

strcpy(temp, fmt); //Copy the Format string

char Format[255];

char *p = strchr(temp,'%');

int i=0;

int iParam;

double fParam;

while(p != NULL)

{

while((*p'z') && (*p!=0) ) p++;

if(*p == 0)break;

p++;

//款式字符串

int nChar = p - temp;

strncpy(Format,temp, nChar);

Format[nChar] = 0;

//参数

if(Format[nChar-1] != 'f')

{

iParam = va_arg( arglist, int);

printf(Format, iParam);

}

else

{

fParam = va_arg( arglist, double);

printf(Format, fParam);

}

i++;

if(*p == 0) break;

strcpy(temp, p);

p = strchr(temp, '%');

}

if(temp[0] != 0)

printf(temp);

return i;

}

void main()

{

int i=10, j=20;

char buf[] = "This is a test";

double f= 123.456;

mywrite("String: %s\nInt: %d, %d\nFloat :%4.2f\nEnd", buf, i, j, f, 0);

t("aaa", i);

}

//输出:

String: This is a test

Int: 10, 20

Float :123.46

End

当然这里的解析是不完善的。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

您可能还会对下面的文章感兴趣: