引數個數可變的函式

2021-05-24 14:24:09 字數 1386 閱讀 5546

今天突然想起引數個數可變的函式,然後想了下,覺得自己還真不太了解它,遂決定弄清楚。

1.如何取得可變引數:

比如 void f(int fixpara1,...){

char * p=(char *)(&fixpara1);//取得固定引數位址

cout<<*((int *)p)為什麼p+=4可以取到第乙個可變引數呢?因為引數在函式被呼叫前,會逐個被壓入棧,然後進入函式體開始執行。

比如,如果我們這樣呼叫: f(4,3,2),那麼函式體中的輸出結果為3。棧中的引數從記憶體低位址單元到高位址單元,依次存放了4,3,2,函式是按照從右往左的順序依次壓入棧中的,先push 2,在push 3,最後把4壓入棧中,而windows系統中執行壓棧操作後,棧頂指標esp會減小(棧往記憶體低端生長),所以,如果我們取得了引數4的位址,那麼,每次往記憶體高階移動4個位元組,就可以依次取得3,2.

2.如何確定取得的可變引數型別:

答案是,基本上,如果你不顯示的告訴f函式,它是沒法知道的。那為何printf中的%d可以識別為乙個整型引數呢?那是因為你都告訴他了,後邊對應的引數型別按照%d(整型)來處理了。所以,通常需要再固定引數中來顯示給出引數型別。printf就是在格式化字串的引數中給出的型別,這個大家都知道

3.如何確定可變引數的個數:

答案是,不可能知道引數個數,除非你在固定引數裡,告訴它有幾個可變引數,比如你讓某個固定引數顯示給出可變引數個數,在函式體中就可以知道可變引數個數了,printf就是通過格式符的個數來確定引數的個數,比如「%d %s",顯然表示有兩個可變引數,

大家可以做實驗,printf("%d %d %d %d",3,4),通常確實會輸出3,4,然後是輸出兩個不確定的整數,但是,這是危險的,因為程式取可變引數就是不停的將可變引數指標遞增來取引數,你告訴他有4個整型引數,他就會不停的取出4個整型引數。如果你的格式串中格式符很多,由於你總是往後去取一些不該本函式取的數,那麼,有可能到了某個位址你就非法訪問啦

4.既然引數個數對應函式體而言是不確定的,那麼,函式就不知道應該在返回前彈出多少個引數,以便達到棧的平衡。比如,你可能f(1,2),也可能f(1,2,3),前者要pop兩次來清理引數,後者要pop 3次來清理引數,函式自身沒法確定要彈出多少次,所以,這時,只能是有函式的呼叫者來自己清理了。比如,通常的函式(stdcall 呼叫方式),函式在返回前會將引數從棧中彈出,進行一些棧的清理,已達到與呼叫前一致,所以

如果你用反彙編檢視函式呼叫過程,會看到 push parg1 ;push parg2...; call f  ;執行後續其他功能;

也就是說,f本身完成了pop 引數1,pop引數2.。。的工作

但是,對應cdecl呼叫方式,你會看到,在call f之後,會有個比如add esp,16 之類,表示彈出棧頂16位元組資料(這說明函式呼叫時壓入的引數總共佔了16個位元組),也就是表示函式呼叫者自己來彈出引數,恢復棧的平衡。

可變引數個數的函式

type vafunction type arg1,type arg2,引數可以分為兩部分 個數確定的固定引數和個數可變的可選引數。函式至少需要乙個固定引數,固定引數的宣告和普通函式一樣 可選引數由於個數不確定,宣告時用 表示。固定引數和可選引數公同構成乙個函式的引數列表。標準c c 包含標頭檔案s...

C 引數個數可變函式的本質

va list是乙個巨集,由va start和va end界定。typedef char va list void va start va list ap,prev param type va arg va list ap,type void va end va list ap 其中,va list...

swift定義引數個數可變的函式

在oc中,當乙個功能豐富的類對外暴露介面時,在.件中往往會提供一系列的api,引數由少到多。但是在.m檔案的實現中,往往都是利用乙個通用的實現。例如 宣告 int addnuma int numa numb int numb int addnuma int numa numb int numb nu...