C/C++ 函數(shù)原理傳參示例詳解
x84-64的寄存器
本文所用gcc為 x86-64 gcc 10.1
wiki.cdot.senecacollege.ca/wiki/X86_64…
rax - register a extended
rbx - register b extended
rcx - register c extended
rdx - register d extended
rbp - register base pointer (start of stack)
rsp - register stack pointer (current location in stack, growing downwards)
rsi - register source index (source for data copies)
rdi - register destination index (destination for data copies)
其他寄存器: r8 r9 r10 r11 r12 r13 r14 r15
函數(shù)是個(gè)什么東西?
一個(gè)簡(jiǎn)單的函數(shù)
int func(){}
int main() {
int x = 2;
func();
}
main:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl $2, -4(%rbp)
call func()
movl $0, %eax
leave
ret
分配空間動(dòng)作如下所示:

這里加了個(gè)函數(shù)調(diào)用是因?yàn)樵谟行r(shí)候,沒(méi)有函數(shù)調(diào)用,就不會(huì)使用subq $16, %rsp 這一條指令,我的猜想是既然你都是棧頂?shù)?,并且不?huì)再有rbp的變化,那么棧頂以上的元素我都可以隨便用。
并且我們觀察可以得知,分配??臻g時(shí),他是分配的16個(gè)字節(jié),也就是說(shuō),有對(duì)齊
返回時(shí),彈出棧頂,就可以恢復(fù)到上一個(gè)棧幀的狀態(tài)了。
傳參姿勢(shì)
入棧規(guī)則
c/c++ 中規(guī)定的函數(shù)壓棧順序是從右到左,當(dāng)然,如果你是 Visual C/C++的話,它們有更多的玩法 比如:
template<typename T>
T val(T t) {
cout << t << endl;
return t;
}
signed main() {
printf("%d%d%d", val(1), val(2), val(3));
return 0;
}
結(jié)果
3
2
1
123
看看匯編
int func(int x, int y, int z) {
return 0;
}
int main() {
func(1, 2, 3);
}
生成的匯編
func(int, int, int):
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl %edx, -12(%rbp)
movl $0, %eax
popq %rbp
ret
main:
pushq %rbp
movq %rsp, %rbp
movl $3, %edx
movl $2, %esi
movl $1, %edi
call func(int, int, int)
movl $0, %eax
popq %rbp
ret
上文中可以看出,也證實(shí)了我們所觀察到的,首先把3傳給了edx,2傳給了esi,1傳給了edi
全都存寄存器嗎?
寄存器畢竟少,當(dāng)然,還可以存在棧上嘛
int fun() {return 0;}
int func(int x, int y, int z, int a, int b, int c, int d, int e, int f){
fun();
return e;
}
int main() {
func(1, 2, 3, 4, 5, 6, 7, 8, 9);
return 0;
}
fun():
pushq %rbp
movq %rsp, %rbp
movl $0, %eax
popq %rbp
ret
func(int, int, int, int, int, int, int, int, int):
pushq %rbp
movq %rsp, %rbp
subq $24, %rsp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl %edx, -12(%rbp)
movl %ecx, -16(%rbp)
movl %r8d, -20(%rbp)
movl %r9d, -24(%rbp)
call fun()
movl 24(%rbp), %eax
leave
ret
main:
pushq %rbp
movq %rsp, %rbp
pushq $9 // 24+%rbp
pushq $8 // 16+%rbp
pushq $7 // 8 +%rbp
movl $6, %r9d
movl $5, %r8d
movl $4, %ecx
movl $3, %edx
movl $2, %esi
movl $1, %edi
call func(int, int, int, int, int, int, int, int, int)
addq $24, %rsp
movl $0, %eax
leave
ret
主函數(shù)中的這三條語(yǔ)句
pushq $9 pushq $8 pushq $7
說(shuō)明了,當(dāng)函數(shù)入棧放寄存器放不下時(shí),會(huì)放在棧上,放在棧頂之上,等函數(shù)調(diào)用執(zhí)行完成后,rbp取出回到當(dāng)前位置之后,再去addq $24, %rsp 把棧彈出這些元素。
并且func函數(shù)中的movl 24(%rbp), %eax也證明了,傳的參數(shù)是在棧頂?shù)纳厦妫ㄗ陨舷蛳略鲩L(zhǎng)) 24 + %rbp 剛好是 $9, 也就是局部變量f的位置
傳對(duì)象呢?
在這里,暫且不談內(nèi)存布局,把一個(gè)對(duì)象看成一塊內(nèi)存對(duì)于的位置
這里用一個(gè)結(jié)構(gòu)體做示例
struct E {int x, y, z;};
E func(E e){
e.x = 2;
return e;
}
int main() {
E e = {.x = 1, .y = 2, .z = 3};
e = func(e);
return 0;
}
func(E):
pushq %rbp
movq %rsp, %rbp
// 將rdi 和 esi 取出來(lái) 放到 rdx 和 eax 中
movq %rdi, %rdx
movl %esi, %eax
// 存放到開辟好的空間中 {x = rbp - 32, y = rbp - 28, z = rbp - 24}
movq %rdx, -32(%rbp)
movl %eax, -24(%rbp)
// 更改 x
movl $2, -32(%rbp)
// 將值移動(dòng)到寄存器上,從返回寄存器上移動(dòng)到局部返回出去的變量
movq -32(%rbp), %rax
movq %rax, -12(%rbp)
movl -24(%rbp), %eax
movl %eax, -4(%rbp)
// 將返回值值移動(dòng)到寄存器上 rax rdx 上
movq -12(%rbp), %rax
movl -4(%rbp), %ecx
movq %rcx, %rdx
popq %rbp
ret
main:
// 壓棧保存現(xiàn)場(chǎng) 沒(méi)什么好說(shuō)的
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
// 內(nèi)存布局
rbp
| z rbp - 4
| y rbp - 8
| x rbp - 12
movl $1, -12(%rbp)
movl $2, -8(%rbp)
movl $3, -4(%rbp)
// 移動(dòng) x 和 y 到 rdx 寄存器中
movq -12(%rbp), %rdx
// 移動(dòng) z 到 eax中
movl -4(%rbp), %eax
// 再將 rdx 和 eax 分別移動(dòng)到rdi 和 esi中
movq %rdx, %rdi
movl %eax, %esi
call func(E)
// 從rax 中取出x y
movq %rax, -12(%rbp)
// 從rdx中取出z
movl -4(%rbp), %eax
andl $0, %eax
orl %edx, %eax //
movl %eax, -4(%rbp)
movl $0, %eax
leave
ret以上就是C/C++ 函數(shù)原理傳參示例詳解的詳細(xì)內(nèi)容,更多關(guān)于C/C++ 函數(shù)原理傳參的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++實(shí)現(xiàn)點(diǎn)云添加高斯噪聲功能
所謂高斯噪聲是指它的概率密度函數(shù)服從高斯分布(即正態(tài)分布)的一類噪聲,這篇文章主要給大家介紹了關(guān)于C++實(shí)現(xiàn)點(diǎn)云添加高斯噪聲功能的相關(guān)資料,需要的朋友可以參考下2021-07-07
C++基礎(chǔ)學(xué)習(xí)之函數(shù)重載的簡(jiǎn)單介紹
函數(shù)重載是一種特殊情況,C++允許在同一作用域中聲明幾個(gè)類似的同名函數(shù),這些同名函數(shù)的形參列表(參數(shù)個(gè)數(shù),類型,順序)必須不同,常用來(lái)處理實(shí)現(xiàn)功能類似數(shù)據(jù)類型不同的問(wèn)題。這篇文章主要給大家介紹了關(guān)于C++基礎(chǔ)學(xué)習(xí)之函數(shù)重載的相關(guān)資料,需要的朋友可以參考下2019-01-01
C語(yǔ)言實(shí)現(xiàn)電子郵件地址驗(yàn)證程序
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)電子郵件地址驗(yàn)證程序,利用的是POSIX正則表達(dá)式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2015-11-11
C++實(shí)現(xiàn)LeetCode(132.拆分回文串之二)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(132.拆分回文串之二),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
如何實(shí)現(xiàn)一定概率選中某一個(gè)字母
本篇文章是對(duì)如何實(shí)現(xiàn)一定概率選中某一個(gè)字母的解決方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05

