對(duì)C/C++可變參數(shù)表的深層探索

字號(hào):

引言
    C/C++語言有一個(gè)不同于其它語言的特性,即其支持可變參數(shù),典型的函數(shù)如printf、scanf等可以接受數(shù)量不定的參數(shù)。如:
    printf ( "I love you" );
    printf ( "%d", a );
    printf ( "%d,%d", a, b );
    第一、二、三個(gè)printf分別接受1、2、3個(gè)參數(shù),讓我們看看printf函數(shù)的原型:
    int printf ( const char *format, ... );
    從函數(shù)原型可以看出,其除了接收一個(gè)固定的參數(shù)format以外,后面的參數(shù)用"…"表示。在C/C++語言中,"…"表示可以接受不定數(shù)量的參數(shù),理論上來講,可以是0或0以上的n個(gè)參數(shù)。
    本文將對(duì)C/C++可變參數(shù)表的使用方法及C/C++支持可變參數(shù)表的深層機(jī)理進(jìn)行探索。
    可變參數(shù)表的用法
    1、相關(guān)宏
    標(biāo)準(zhǔn)C/C++包含頭文件stdarg.h,該頭文件中定義了如下三個(gè)宏:
    void va_start ( va_list arg_ptr, prev_param ); /* ANSI version */
    type va_arg ( va_list arg_ptr, type );
    void va_end ( va_list arg_ptr );
    在這些宏中,va就是variable argument(可變參數(shù))的意思;arg_ptr是指向可變參數(shù)表的指針;prev_param則指可變參數(shù)表的前一個(gè)固定參數(shù);type為可變參數(shù)的類型。va_list也是一個(gè)宏,其定義為typedef char * va_list,實(shí)質(zhì)上是一char型指針。char型指針的特點(diǎn)是++、--操作對(duì)其作用的結(jié)果是增1和減1(因?yàn)閟izeof(char)為1),與之不同的是int等其它類型指針的++、--操作對(duì)其作用的結(jié)果是增sizeof(type)或減sizeof(type),而且sizeof(type)大于1。
    通過va_start宏我們可以取得可變參數(shù)表的首指針,這個(gè)宏的定義為:
    #define va_start ( ap, v ) ( ap = (va_list)&v + _INTSIZEOF(v) )
    顯而易見,其含義為將最后那個(gè)固定參數(shù)的地址加上可變參數(shù)對(duì)其的偏移后賦值給ap,這樣ap就是可變參數(shù)表的首地址。其中的_INTSIZEOF宏定義為:
    #define _INTSIZEOF(n) ((sizeof ( n ) + sizeof ( int ) - 1 ) & ~( sizeof( int ) - 1 ) )
    va_arg宏的意思則指取出當(dāng)前arg_ptr所指的可變參數(shù)并將ap指針指向下一可變參數(shù),其原型為:
    #define va_arg(list, mode) ((mode *)(list =\
    (char *) ((((int)list + (__builtin_alignof(mode)<=4?3:7)) &\
    (__builtin_alignof(mode)<=4?-4:-8))+sizeof(mode))))[-1]
    對(duì)這個(gè)宏的具體含義我們將在后面深入討論。
    而va_end宏被用來結(jié)束可變參數(shù)的獲取,其定義為:
    #define va_end ( list )
    可以看出,va_end ( list )實(shí)際上被定義為空,沒有任何真實(shí)對(duì)應(yīng)的代碼,用于代碼對(duì)稱,與va_start對(duì)應(yīng);另外,它還可能發(fā)揮代碼的"自注釋"作用。所謂代碼的"自注釋",指的是代碼能自己注釋自己。
    下面我們以具體的例子來說明以上三個(gè)宏的使用方法。
    2、一個(gè)簡(jiǎn)單的例子
    #include
    /* 函數(shù)名:max
    * 功能:返回n個(gè)整數(shù)中的值
    * 參數(shù):num:整數(shù)的個(gè)數(shù) ...:num個(gè)輸入的整數(shù)
    * 返回值:求得的整數(shù)
    */
    int max ( int num, ... )
    {
    int m = -0x7FFFFFFF; /* 32系統(tǒng)中最小的整數(shù) */
    va_list ap;
    va_start ( ap, num );
    for ( int i= 0; i< num; i++ )
    {
    int t = va_arg (ap, int);
    if ( t > m )
    {
    m = t;
    }
    }
    va_end (ap);
    return m;
    }
    /* 主函數(shù)調(diào)用max */
    int main ( int argc, char* argv[] )
    {
    int n = max ( 5, 5, 6 ,3 ,8 ,5); /* 求5個(gè)整數(shù)中的值 */
    cout << n;
    return 0;
    }
    函數(shù)max中首先定義了可變參數(shù)表指針ap,而后通過va_start ( ap, num )取得了參數(shù)表首地址(賦給了ap),其后的for循環(huán)則用來遍歷可變參數(shù)表。這種遍歷方式與我們?cè)跀?shù)據(jù)結(jié)構(gòu)教材中經(jīng)??吹降谋闅v方式是類似的。
    函數(shù)max看起來簡(jiǎn)潔明了,但是實(shí)際上printf的實(shí)現(xiàn)卻遠(yuǎn)比這復(fù)雜。max函數(shù)之所以看起來簡(jiǎn)單,是因?yàn)椋?BR>    (1) max函數(shù)可變參數(shù)表的長度是已知的,通過num參數(shù)傳入;
    (2) max函數(shù)可變參數(shù)表中參數(shù)的類型是已知的,都為int型。
    而printf函數(shù)則沒有這么幸運(yùn)。首先,printf函數(shù)可變參數(shù)的個(gè)數(shù)不能輕易的得到,而可變參數(shù)的類型也不是固定的,需由格式字符串進(jìn)行識(shí)別(由%f、%d、%s等確定),因此則涉及到可變參數(shù)表的更復(fù)雜應(yīng)用。
    下面我們以實(shí)例來分析可變參數(shù)表的高級(jí)應(yīng)用。