可變參數函數

函數聲明:

  • 返回類型 函數名(已知類型,個數的參數列表,Type parmN,...);數組

  • parmN是最右邊的已知類型的參數名;如:app

void f(int num,...)
/** parmN就是"num" */




va_start

/**
 * 初始化ap,必須先於va_arg(),va_end(),va_copy被調用.
 * @param ap va_list類型變量.
 * @param parmN 見上
 * @warning 因爲須要parmN的地址來初始化ap,因此表達式'&(parmN)'應該是合法的,即
 *         parmN不能夠是寄存器變量,數組,函數.
 * */
void va_start(ap,parmN)

va_arg

/**
 * 返回下一個參數的值.
 * va_arg()宏展開後是一個表達式,該表達式的結果就是下一個參數的值.每一次調用都會更改ap的狀態,從而完成遍歷參數列表的工做
 * @param ap va_start()初始過的va_list變量
 * @param type 下一個參數的類型
 * @warning 指向type類型變量的指針類型應該是'type *';如type可取int,double...可是type不能夠是數組類型,函數類型
 * @warning 受'默認實際參數提高'規則影響,type的值不多是:
 *         char,unsigned char,signed char(若是指望參數類型爲這些,則type是int)
 *         short,unsigned short,unsigned short,short int,unsigend short int,signed short int(若是指望參數類型爲這些,則type是int)
 *         float(若是指望參數類型爲這個,則type是double)
 * */
type va_arg(ap,type)

默認參數提高規則

在調用一個不帶原型聲明的函數以前,編譯器會對每一個實參執行"默認實際參數提高"後在傳遞給函數。具體規則以下函數

  • char、short和相應的signed、unsigned類型的實際參數提高到int,若是int不能存儲原值,則提高到unsigned intspa

  • float類型的實際參數將提高到double指針

一樣在調用具備可變參數列表的函數時,也會運用'參數提高規則',因此在函數內部是不會出現char,short,float類型的參數的!因此Type的值不多是...code

va_end


/** 在結束對可變參數列表的遍歷後調用 */
va_end(ap)

ap在函數之間的傳遞

在調用va_start()對ap進行初始化以後,能夠在函數中或者她調用的其餘函數中執行va_arg()遍歷可變參數原型


/**
 * 遍歷並打印可變參數列表.
 * @param num 可變參數列表中參數個數
 * @param ap 由調用者傳遞,ap中存放若干個int類型的參數 */
void printArgs(int num,va_list ap){
    printf("Begin...\n");
    while(--num >= 0)
        printf("%d\t",va_arg(ap,int));
    printf("\nOver...\n");
    return ;
}

void f1(int num,...){
    va_list ap;
    va_start(ap,num);
    printArgs(num,ap);
//    printArgs(num,ap);/* 能夠驗證ap的狀態被改變了 */
    va_end(ap);
    return ;
}


傳值/傳址?

  • 如上在printArgs()中因爲使用了va_arg因此會更改ap的狀態,那麼在f1調用printArgs()以後f1中的ap的狀態是否會變化?編譯器

  • 根據對va_list實現的不一樣,ap可能採用傳值或者傳址調用,即沒法保障f1中ap必定不變it

如上函數,運行後f1中ap的狀態被改變了io

確保不被共享

va_copy


/**
 * copies the (previously initialized) variable argument list src to dest.
 * 行爲就像:
 *     va_start(des,parmN);            
 *     [va_start()  were  applied to dest with the same last argument]
 *     若干次的va_arg(des,type)調用        
 *     [followed by the same number of va_arg() invocations that was used to reach the current state of src.]
 * */
void va_copy(va_list des,va_list src);

void printArgs(int num,va_list ap){
	va_list xap;
	va_copy(xap,ap);	/* 執行一次copy操做 */
	printf("Begin...\n");
	while(--num >= 0)
		printf("%d\t",va_arg(xap,int));/* 使用xap遍歷參數列表,不會改變ap的狀態 */
	printf("\nOver...\n");
	va_end(xap);
	return ;
}

void f1(int num,...){
	va_list ap;
	va_start(ap,num);
	printArgs(num,ap);
	printArgs(num,ap);/* 能夠驗證ap的狀態不會被改變 */
	va_end(ap);
	return ;
}


確保共享


void printArgs(int num,va_list *ap){    /* 傳遞ap的地址 */
    printf("Begin...\n");
    while(--num >= 0)
        printf("%d\t",va_arg(*ap,int));    /* 解引用 */
    printf("\nOver...\n");
    va_end(*ap);
    return ;
}

void f1(int num,...){
    va_list ap;
    va_start(ap,num);
    printArgs(num,&ap);
    printArgs(num,&ap);
    va_end(ap);
    return ;
}
相關文章
相關標籤/搜索