返回類型 函數名(已知類型,個數的參數列表,Type parmN,...);數組
parmN是最右邊的已知類型的參數名;如:app
void f(int num,...) /** parmN就是"num" */
/** * 初始化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()宏展開後是一個表達式,該表達式的結果就是下一個參數的值.每一次調用都會更改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(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
/** * 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 ; }