函数入参分析
函數(shù)入?yún)⒎治?/h3>- 背景
- 固定參數(shù)
- 可變參數(shù)
- 總結(jié)
- 固定參數(shù)
- 可變參數(shù)
- 總結(jié)
背景
一般來(lái)說(shuō),我們調(diào)用一個(gè)函數(shù)的時(shí)候,需要傳入對(duì)應(yīng)數(shù)據(jù)類型和個(gè)數(shù)的參數(shù),例如調(diào)用int sum(int x,int y);我們需要傳入兩個(gè)int類型的數(shù),且只能傳入兩個(gè)int類型的數(shù)。如果類型不匹配或者個(gè)數(shù)不匹配,編譯器都會(huì)報(bào)warning或者error。我們將int x ,int y稱作形參,下面就來(lái)分析以下參數(shù)是怎么傳遞給被調(diào)用函數(shù)的。
備注:
測(cè)試的平臺(tái)x86_64-redhat-linux
固定參數(shù)
固定參數(shù)指的是,一個(gè)函數(shù)的形參個(gè)數(shù)是確定的,如上面舉例的int sum(int x,int y); 它的參數(shù)個(gè)數(shù)是2,是一個(gè)固定值。還有一種參數(shù)個(gè)數(shù)是可變的,例如printf函數(shù),對(duì)于可變參數(shù)入?yún)⒃谙乱还?jié)中分析。
大部分的課本或者圖書(shū)上說(shuō),函數(shù)入?yún)⒌捻樞蚴菑挠彝笠来螇喝霔V?#xff0c;由于棧的增長(zhǎng)是由高地址向低地址增長(zhǎng),故可以得知越是靠近右邊的參數(shù),所處的地址應(yīng)該是越大的。例如my_printf(char* fmt,int a,int b,int c),入棧的順序:c->b->a->fmt(指針),地址的大小也是c->b->a->fmt(指針)。
我寫(xiě)了一個(gè)測(cè)試程序來(lái)驗(yàn)證這個(gè)說(shuō)法,這個(gè)測(cè)試程序把函數(shù)的入?yún)⒌刂反蛴〕鰜?lái)了:
運(yùn)行的結(jié)果是:
addr: fmt:0x7ffcd7e3d978 a:0x7ffcd7e3d974 b:0x7ffcd7e3d970 c:0x7ffcd7e3d96c從運(yùn)行的結(jié)果看,c的地址最小(0x7ffcd7e3d96c),fmt的地址最大(0x7ffcd7e3d978 ),和大多數(shù)的資料上描述不相符。原因?qū)嶋H是:
函數(shù)的入?yún)㈨樞蚝途幾g器有關(guān),編譯器決定函數(shù)怎么入?yún)?#xff0c;我測(cè)試的平臺(tái)是x86_64-redhat-linux,大多數(shù)資料講的平臺(tái)是32位平臺(tái),所以會(huì)有差異。
下面通過(guò)objdump生成匯編代碼來(lái)分析參數(shù)入棧的過(guò)程:
可以看出,main函數(shù)僅僅做了寫(xiě)入寄存器的操作
my_printf將寄存器中的參數(shù)寫(xiě)入到棧中:
可變參數(shù)
按照我的理解,可變參函數(shù)入?yún)?yīng)該和正常的函數(shù)一樣,都會(huì)依次將參數(shù)壓入到棧中,也會(huì)存儲(chǔ)到寄存器中。為了驗(yàn)證這個(gè)想法,所以做了一個(gè)實(shí)驗(yàn):
1)編寫(xiě)測(cè)試代碼
2)gdb加載測(cè)試代碼
3)查看棧幀信息,可以看到args中只有num,并沒(méi)有看到后面的入?yún)?,2,3
4)查看寄存器,在寄存器中可以看到入?yún)?,2,3
(gdb) info registers rax 0x0 0 rbx 0x0 0 rcx 0x3 3 rdx 0x2 2 rsi 0x1 1 rdi 0x3 3可變參數(shù)在gdb上表現(xiàn)不出來(lái),實(shí)際也是壓入到棧中了
把棧內(nèi)存打印出來(lái),棧中有參數(shù),但是參數(shù)2,3,4和參數(shù)1的位置并不是緊鄰的,從匯編代碼中也可以看出來(lái),以下是棧的內(nèi)容:
網(wǎng)上的資料和課本上所說(shuō)的va_list va_arg等,都是在x86平臺(tái)上,對(duì)于x64平臺(tái)上來(lái)說(shuō),實(shí)現(xiàn)完全不一樣。X86平臺(tái)的va_list是一個(gè)char*類型,但是在我的平臺(tái)上,va_list是一個(gè)結(jié)構(gòu)體,從gdb可以看出來(lái),p_args是va_list類型的變量,它有g(shù)p_offset,fp_offset,overflow_arg_area,reg_save_area成員。所以x86和x64的參數(shù)入?yún)⑹遣灰粯拥?#xff0c;直接套用x86的參數(shù)入?yún)?huì)得不到想要的結(jié)果:
總結(jié)
平臺(tái)不同,編譯器不同會(huì)導(dǎo)致入?yún)⒌捻樞虿煌?#xff0c;課本和資料上所說(shuō)的,可能和實(shí)際情況不相符,所以只能作為參考,具體情況還需要具體分析。通過(guò)分析匯編代碼,會(huì)讓你對(duì)入?yún)⒂幸粋€(gè)深入的了解。
總結(jié)
- 上一篇: java 入参校验_java开发参入参数
- 下一篇: 需求分析挑战之旅(疯狂的订餐系统)(4)