面试题分析 - 从汇编角度理解虚函数与虚表
前言:
最近在逛群的时候遇到群友的一个面试题感觉很有趣这里记录一下分析过程
问题:以下程序能否正常编译运行,如果能运行输出什么
class A {
public:
void f1 () {
printf ("A::f1\n");
}
virtual void f2 () {
printf ("A::f2\n");
}
};
class B {
public:
virtual void f1 () {
printf ("B::f1\n");
}
virtual void f2 () {
printf ("B::f2\n");
}
};
int main () {
B b;
A *p = (A *)&b;
p->f1 ();
p->f2 ();
}
分析
环境:
编译环境:wsl
工具:gcc gdb
详细分析
首先,通过gdb运行程序, 我们把程序断点到29行

通过查看汇编 29行主要做了两个事情 初始化b的vptr ,来看一下两段汇编代码
lea 0xb445(%rip),%rax 将虚表地址存入 寄存器 rax
%rax,-0x18(%rbp) 将 虚表地址存入 寄存器rbp 偏移 -0x18的地址出 可以看出这里就是b的首地址也是vpt的地址,这里将vpt指向了虚表

然后再看第30行, 将a指向 b的地址

再看第31行 这里开始调用类A的f1函数,由于类A的f1不是虚函数所以并不会在编译后不会查询虚表而是直接调用 .text段的函数

最后再看调用A的f2函数,通过汇编可以看到
mov -0x10(%rbp),%rax 这里将vpt的地址存入 寄存器rax
mov (%rax),%rax 这里 相当于将 *vpt 将虚表的地址存入rax
mov (%rax),%rdx 这里相当于将 虚表第第一个地址存入 rax, 通过上面的分析可以知道,a指向的是b的地址,所以这里取的是类B的虚函数表地址,而b的虚函数表第一个地址存入的是类声明虚函数时存入的 B::f1的地址,所以这里真实取的是B::f1
mov -0x10(%rbp),%rax mov %rax,%rdi 这里相当于将this指针传入函数的第一个参数
call *%rdx 调用虚函数
最后输出:
A::f1
B::f1
6838

被折叠的 条评论
为什么被折叠?



