?
快捷搜索:  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

當然這里的解析是不完善的。

免責聲明:以上內容源自網絡,版權歸原作者所有,如有侵犯您的原創版權請告知,我們將盡快刪除相關內容。

您可能還會對下面的文章感興趣:

快三平台开户