新聞中心

        EEPW首頁 > C/C++可變參數函數

        C/C++可變參數函數

        ——
        作者: 時間:2007-05-10 來源: 收藏

        c/c++支持,即的參數是不確定的。

        一、為什么要使用

        一般我們編程的時候,函數中形式參數的數目通常是確定的,在調用時要依次給出與形式參數對應的所有實際參數。但在某些情況下希望函數的參數個數可以根據需要確定,因此c語言引入函數。這也是c功能強大的一個方面,其它某些語言,比如fortran就沒有這個功能。

        典型的可變參數函數的例子有大家熟悉的printf()、scanf()等。

        二、c/c++如何實現可變參數的函數?

        為了支持可變參數函數,語言引入新的調用協議, 即語言調用約定 __cdecl 。 采用/語言編程的時候,默認使用這個調用約定。如果要采用其它調用約定,必須添加其它關鍵字聲明,例如WIN32 API使用PASCAL調用約定,函數名字之前必須加__stdcall關鍵字。

        采用C調用約定時,函數的參數是從右到左入棧,個數可變。由于函數體不能預先知道傳進來的參數個數,因此采用本約定時必須由函數調用者負責堆棧清理。舉個例子:

        //C調用約定函數
        int __cdecl Add(int a, int b)
        {
        return (a + b);
        }

        函數調用:
        Add(1, 2);
        //匯編代碼是:
        push 2 ;參數b入棧
        push 1 ;參數a入棧
        call @Add ;調用函數。其實還有編譯器用于定位函數的表達式這里把它省略了
        add esp,8 ;調用者負責清棧

        如果調用函數的時候使用的調用協議和函數原型中聲明的不一致,就會導致棧錯誤,這是另外一個話題,這里不再細說。

        另外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(…);

        但是,這樣的話,我們就無法使用函數的參數了,因為無法通過上面所講的宏來提取每個參數。所以除非你的函數代碼中的確沒有用到參數表中的任何參數,否則必須在參數表中使用至少一個普通參數。

        注意,可變參數只能位于函數參數表的最后。不能這樣:

        void MyFunc(…, int i);

        2、采用與UNIX 兼容系統下的聲明方式時,參數個數可變的函數原型是:

        type funcname(va_alist);

        但是要求函數實現的時候,函數名字后面必須加上va_dcl。例如:

        #include
        int average( va_list );

        void main( void )
        {
        。。。//代碼
        }

        /* UNIX兼容形式*/
        int average( va_alist )
        va_dcl
        {
        。。。//代碼
        }

        這種形式不需要提供任何普通的形式參數。type是函數返回值的類型。va_dcl是對函數原型聲明中參數va_alist的詳細聲明,實際是一個宏定義。根據平臺的不同,va_dcl的定義稍有不同。

        在varargs.h中,va_dcl的定義后面已經包括了一個分號。因此函數實現的時候,va_dcl后不再需要加上分號了。

        3、采用頭文件stdarg.h編寫的程序是符合ANSI標準的,可以在各種操作系統和硬件上運行;而采用頭文件varargs.h的方式僅僅是為了與以前的程序兼容,兩種方式的基本原理是一致的,只是在語法形式上有一些細微的區別。 所以一般編程的時候使用stdarg.h。下面的所有例子代碼都采用ANSI標準格式。

        四、可變參數函數的基本使用方法

        下面通過若干例子,說明如何實現可變參數函數的定義和調用。

        //================================ 例子程序1 ===============
        #include < stdio.h >
        #include < string.h >
        #include < stdarg.h >

        /* 函數原型聲明,至少需要一個確定的參數,注意括號內的省略號 */
        int demo( char *, … );

        void main( void )
        {
        demo(”DEMO”, “This”, “is”, “a”, “demo!”, “

        主站蜘蛛池模板: 仙游县| 三亚市| 大新县| 洛宁县| 崇礼县| 保亭| 贡觉县| 屏南县| 高雄县| 乐陵市| 阿拉善右旗| 汉川市| 玉树县| 东港市| 长葛市| 永平县| 昂仁县| 南溪县| 贡觉县| 镇远县| 龙山县| 永安市| 嘉荫县| 玉田县| 连云港市| 辽阳县| 张掖市| 镇安县| 汝阳县| 时尚| 夏津县| 敦煌市| 弋阳县| 叶城县| 雷州市| 伊宁市| 涡阳县| 泸溪县| 西吉县| 黔东| 长岭县|