函數(shù)參數(shù)不確定時(shí)用cstdarg(stdarg.h)

字號(hào):

◎用法:
    func( Type para1, Type para2, Type para3, ... )
    {
    /****** Step 1 ******/
    va_list ap;
    va_start( ap, para3 ); //一定要“...”之前的那個(gè)參數(shù)
    /****** Step 2 ******/
    //此時(shí)ap指向第一個(gè)可變參數(shù)
    //調(diào)用va_arg取得里面的值
    Type xx = va_arg( ap, Type );
    //Type一定要相同,如:
    //char *p = va_arg( ap, char *);
    //int i = va_arg( ap, int );
    //如果有多個(gè)參數(shù)繼續(xù)調(diào)用va_arg
    /****** Step 3 ******/
    va_end(ap); //For robust!
    }
    ◎研究:
    typedef char * va_list;
    #define va_start _crt_va_start
    #define va_arg _crt_va_arg
    #define va_end _crt_va_end
    #define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
    #define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
    #define _crt_va_end(ap) ( ap = (va_list)0 )
    va_list argptr;
    C語言的函數(shù)是從右向左壓入堆棧的,調(diào)用va_start后,
    按定義的宏運(yùn)算,_ADDRESSOF得到v所在的地址,然后這個(gè)
    地址加上v的大小,則使ap指向第一個(gè)可變參數(shù)如圖:
    棧底 高地址
    | .......
    | 函數(shù)返回地址
    | .......
    | 函數(shù)最后一個(gè)參數(shù)
    | ....
    | 函數(shù)第一個(gè)可變參數(shù) <--va_start后ap指向
    | 函數(shù)最后一個(gè)固定參數(shù)
    | 函數(shù)第一個(gè)固定參數(shù)
    棧頂 低地址
    然后,用va_arg()取得類型t的可變參數(shù)值, 先是讓ap指向下一個(gè)參數(shù):
    ap += _INTSIZEOF(t),然后在減去_INTSIZEOF(t),使得表達(dá)式結(jié)果為
    ap之前的值,即當(dāng)前需要得到的參數(shù)的地址,強(qiáng)制轉(zhuǎn)換成指向此參數(shù)的
    類型的指針,然后用*取值
    最后,用va_end(ap),給ap初始化,保持健壯性。
    example:(chenguiming)
    #include
    #include
    #include
    #include
    int average( int first, ... ) //變參數(shù)函數(shù),C++里也有
    {
    int count=0,i=first,sum=0;
    va_list maker; //va_list 類型數(shù)據(jù)可以保存函數(shù)的所有參數(shù),做為一個(gè)列表一樣保存
    va_start(maker,first); //設(shè)置列表的起始位置
    while(i!=-1)
    {
    sum+=i;
    count++;
    i=va_arg(maker,int);//返回maker列表的當(dāng)前值,并指向列表的下一個(gè)位置
    }
    return sum/count;
    }
    void main(void)
    {
    printf( "Average is: %d\n", average( 2, 3, 4,4, -1 ) );
    }
    Linux下的stdarg.h
    #ifndef _STDARG_H
    #define _STDARG_H
    typedef char *va_list; /* 定義va_list 是一個(gè)字符指針類型*/
    /* Amount of space required in an argument list for an arg of type TYPE.
    TYPE may alternatively be an expression whose type is used. */
    /* 下面給出了類型為TYPE 的arg 參數(shù)列表所要求的空間容量。
    TYPE 也可以是使用該類型的一個(gè)表達(dá)式 */
    // 下面這句定義了取整后的TYPE 類型的字節(jié)長度值。是int 長度(4)的倍數(shù)。
    #define __va_rounded_size(TYPE) \
    (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))
    // 下面這個(gè)函數(shù)(用宏實(shí)現(xiàn))使AP 指向傳給函數(shù)的可變參數(shù)表的第一個(gè)參數(shù)。
    // 在第一次調(diào)用va_arg 或va_end 之前,必須首先調(diào)用該函數(shù)。
    // 17 行上的__builtin_saveregs()是在gcc 的庫程序libgcc2.c 中定義的,用于保存寄存器。
    // 它的說明可參見gcc 手冊(cè)章節(jié)“Target Description Macros”中的
    // “Implementing the Varargs Macros”小節(jié)。
    #ifndef __sparc__
    #define va_start(AP, LASTARG) \
    (AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
    #else
    #define va_start(AP, LASTARG) \
    (__builtin_saveregs (), \
    AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
    #endif
    // 下面該宏用于被調(diào)用函數(shù)完成一次正常返回。va_end 可以修改AP 使其在重新調(diào)用
    // va_start 之前不能被使用。va_end 必須在va_arg 讀完所有的參數(shù)后再被調(diào)用。
    void va_end (va_list); /* Defined in gnulib *//* 在gnulib 中定義 */
    #define va_end(AP)
    // 下面該宏用于擴(kuò)展表達(dá)式使其與下一個(gè)被傳遞參數(shù)具有相同的類型和值。
    // 對(duì)于缺省值,va_arg 可以用字符、無符號(hào)字符和浮點(diǎn)類型。
    // 在第一次使用va_arg 時(shí),它返回表中的第一個(gè)參數(shù),考試.大提示后續(xù)的每次調(diào)用都將返回表中的
    // 下一個(gè)參數(shù)。這是通過先訪問AP,然后把它增加以指向下一項(xiàng)來實(shí)現(xiàn)的。
    // va_arg 使用TYPE 來完成訪問和定位下一項(xiàng),每調(diào)用一次va_arg,它就修改AP 以指示
    // 表中的下一參數(shù)。
    #define va_arg(AP, TYPE) \
    (AP += __va_rounded_size (TYPE), \
    *((TYPE *) (AP - __va_rounded_size (TYPE))))
    #endif /* _STDARG_H */