-- TOC --
总结:class type对象的this指针,指向对象所在内存块的起始地址,这个地址同时也是对象第1个成员变量的地址。访问对象的成员变量,就是在this指针加上offset。而成员函数,在编译器看来,跟普通函数接口没有太大区别,只是成员函数接口都有1个隐藏的入参 ,第1个入参,this指针。因此,只能通过实例化后的对象来调用成员函数,这样才有this指针。这也就理解了,static成员函数可以通过class name来调用,因为它没有this指针这个隐藏的入参。
测试用C++代码如下:
#include <cstdio>
using namespace std;
struct you{
int a;
char b;
long c;
you(int a, char b, long c):
a{a},b{b},c{c}{}
void show(){
printf("%d %c %ld\n", a, b, c);
}
};
int main(){
you y{1, '2', 3};
y.show();
return 0;
}
不开优化,汇编:
you::you(int, char, long) [base object constructor]:
push rbp
mov rbp, rsp
# 将第1个参数,对象y的地址,
# 存入此接口stack的高8bytes,
# 然后才是入参
mov QWORD PTR [rbp-8], rdi
mov DWORD PTR [rbp-12], esi
mov eax, edx
mov QWORD PTR [rbp-24], rcx
mov BYTE PTR [rbp-16], al
# 取对象y的地址到rax
mov rax, QWORD PTR [rbp-8]
# 取参数a到edx
mov edx, DWORD PTR [rbp-12]
# 将参数a存入对象y的起始地址,这个地址也是y.a的地址
mov DWORD PTR [rax], edx
mov rax, QWORD PTR [rbp-8]
# 取参数b,无符号扩展到edx
movzx edx, BYTE PTR [rbp-16]
# 将b存入y.b
mov BYTE PTR [rax+4], dl
mov rax, QWORD PTR [rbp-8]
mov rdx, QWORD PTR [rbp-24]
# 将c存入y.c
mov QWORD PTR [rax+8], rdx
nop
pop rbp
ret
.LC0:
.string "%d %c %ld\n"
you::show():
push rbp
mov rbp, rsp
sub rsp, 16
# 对象y的地址,在此stack frame的高位
# this指针
mov QWORD PTR [rbp-8], rdi
# 下面的代码,都是取y的地址到rax,
# 然后通过rax+offset计算a,b,c的地址
mov rax, QWORD PTR [rbp-8]
mov rcx, QWORD PTR [rax+8]
mov rax, QWORD PTR [rbp-8]
movzx eax, BYTE PTR [rax+4]
movsx edx, al
mov rax, QWORD PTR [rbp-8]
mov eax, DWORD PTR [rax]
mov esi, eax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
nop
leave
ret
main:
push rbp
mov rbp, rsp
# 在stack中开辟16bytes,
# 这块空间,就是对象y,
# 从定义来看,对象y需要16bytes空间。
sub rsp, 16
# 取对象y的地址到rax
lea rax, [rbp-16]
mov ecx, 3
mov edx, 50
mov esi, 1
# 调用y的constructor接口,
# 第1个参数是对象y的地址
mov rdi, rax
call you::you(int, char, long) [complete object constructor]
# 调用y的show接口,
# 虽然show没有参数,但依然要把y的地址传入,
# 这就是this指针!
lea rax, [rbp-16]
mov rdi, rax
call you::show()
mov eax, 0
leave
ret
-Wreorder
reorder只是个warning,我想看看当出现此warning的时候,汇编是什么样的。
修改一行代码:
you(int a, char b, long c):
a{a},c{c},b{b}{}
出现warning,但得到的汇编一模一样!
仔细看看这个warning:
<source>:7:10: warning: 'you::c' will be initialized after [-Wreorder]
7 | long c;
| ^
<source>:6:10: warning: 'char you::b' [-Wreorder]
6 | char b;
| ^
<source>:8:5: warning: when initialized here [-Wreorder]
8 | you(int a, char b, long c):
| ^~~
you::c will be initialized after char you::b when initialized here...
所以,我理解,constructor要按照申明顺序初始化对象成员变量,因此会有这个warning。只是个warning,不一定就是个错误!
new & delete
继续修改C++代码:
int main(){
you *y{ new you{1, '2', 3} };
y->show();
delete y;
return 0;
}
汇编如下:
main:
push rbp
mov rbp, rsp
push rbx
# 扩展24bytes,是为了16bytes对齐
sub rsp, 24
# 16是struct you的size大小
mov edi, 16
call operator new(unsigned long)
# 将申请的地址存入rbx
mov rbx, rax
mov ecx, 3
mov edx, 50
mov esi, 1
mov rdi, rbx
call you::you(int, char, long) [complete object constructor]
mov QWORD PTR [rbp-24], rbx
mov rax, QWORD PTR [rbp-24]
mov rdi, rax
call you::show()
mov rax, QWORD PTR [rbp-24]
# 检查对象指针是否为0,
# 如果是,跳过operator delete
test rax, rax
je .L4
# operator delete的第2个参数
mov esi, 16
mov rdi, rax
call operator delete(void*, unsigned long)
.L4:
mov eax, 0
# pop rbx
mov rbx, QWORD PTR [rbp-8]
leave
ret
对operator的调用,也如函数调用一般。
operator delete自带0指针检查,与free(0)
不同的是,后者对0的检查,在接口内部。
本文链接:https://cs.pynote.net/hd/asm/202302121/
-- EOF --
-- MORE --