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ù)是個什么東西?
一個簡單的函數(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
分配空間動作如下所示:

這里加了個函數(shù)調(diào)用是因為在有些時候,沒有函數(shù)調(diào)用,就不會使用subq $16, %rsp 這一條指令,我的猜想是既然你都是棧頂?shù)?,并且不會再有rbp的變化,那么棧頂以上的元素我都可以隨便用。
并且我們觀察可以得知,分配??臻g時,他是分配的16個字節(jié),也就是說,有對齊
返回時,彈出棧頂,就可以恢復到上一個棧幀的狀態(tài)了。
傳參姿勢
入棧規(guī)則
c/c++ 中規(guī)定的函數(shù)壓棧順序是從右到左,當然,如果你是 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;
}
結果
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
上文中可以看出,也證實了我們所觀察到的,首先把3傳給了edx,2傳給了esi,1傳給了edi
全都存寄存器嗎?
寄存器畢竟少,當然,還可以存在棧上嘛
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ù)中的這三條語句
pushq $9 pushq $8 pushq $7
說明了,當函數(shù)入棧放寄存器放不下時,會放在棧上,放在棧頂之上,等函數(shù)調(diào)用執(zhí)行完成后,rbp取出回到當前位置之后,再去addq $24, %rsp 把棧彈出這些元素。
并且func函數(shù)中的movl 24(%rbp), %eax也證明了,傳的參數(shù)是在棧頂?shù)纳厦妫ㄗ陨舷蛳略鲩L) 24 + %rbp 剛好是 $9, 也就是局部變量f的位置
傳對象呢?
在這里,暫且不談內(nèi)存布局,把一個對象看成一塊內(nèi)存對于的位置
這里用一個結構體做示例
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 取出來 放到 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)
// 將值移動到寄存器上,從返回寄存器上移動到局部返回出去的變量
movq -32(%rbp), %rax
movq %rax, -12(%rbp)
movl -24(%rbp), %eax
movl %eax, -4(%rbp)
// 將返回值值移動到寄存器上 rax rdx 上
movq -12(%rbp), %rax
movl -4(%rbp), %ecx
movq %rcx, %rdx
popq %rbp
ret
main:
// 壓棧保存現(xiàn)場 沒什么好說的
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)
// 移動 x 和 y 到 rdx 寄存器中
movq -12(%rbp), %rdx
// 移動 z 到 eax中
movl -4(%rbp), %eax
// 再將 rdx 和 eax 分別移動到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ù)原理傳參示例詳解的詳細內(nèi)容,更多關于C/C++ 函數(shù)原理傳參的資料請關注腳本之家其它相關文章!
相關文章
C++ 動態(tài)創(chuàng)建按鈕及 按鈕的消息響應
這篇文章主要介紹了C++ 動態(tài)創(chuàng)建按鈕及 按鈕的消息響應的相關資料,需要的朋友可以參考下2015-06-06

