前言
先说结论:虚函数表存储在只读数据段(.rodata
)、虚函数存储在代码段(.text
)、虚表指针的存储的位置与对象存储的位置相同,可能在栈、也可能在堆或数据段等。
相信大家知道虚表指针和虚函数存储的位置,但对于虚函数表的存储位置一时无法确定。看到这个问题时,我也是蒙的,查阅资料后得知虚函数表存储在只读数据段。但网上一些验证的方法,由于编译器原因无法复现,只能从其他角度推论得出。
反汇编
环境:gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04)
验证代码:
#include <iostream>
using namespace std;
class Q {
public:
virtual void Test() {
cout << "Test" << endl;
}
};
class Qgw : public Q {
public:
virtual void Test() {
cout << "Test Position" << endl;
}
};
int main() {
Q* q = new Qgw;
q->Test();
return 0;
}
并不采用打印地址的方式,因为打印出的地址与反汇编得到的地址不同,无法直接得出结果。
首先编译代码:g++ -o test test.cc -O0 -m32
,以 32 位方式编译。
然后将符号表输出到文件:objdump -tC test > test.txt
。
打开 test.txt 可以找到以下内容:
000012fc w F .text 00000044 Qgw::Test()
0000201c w O .rodata 00000005 typeinfo name for Qgw
00003ea8 w O .data.rel.ro 0000000c typeinfo for Qgw
00003e90 w O .data.rel.ro 0000000c vtable for Qgw
第一行比较清晰,说明 Qgw::Test()
存储在 .text
段,也就说明虚函数和普通函数一样,都存储在代码段。
下面几行又是什么东西呢?
根据《深度探索 C++ 对象模型》的 C++ 对象模型可知,typeinfo 是存储在虚函数表中,用来获取对象类型信息的。最下面的 .data.rel.ro
是只读数据段的重定位段,在链接时重定位。由此,可以推出虚函数表是存储在只读数据段的。
相近地址
也可以采用打印地址的方式,与已知的一些段地址比较,看虚函数表地址和哪个段地址更接近。
环境:gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04)
验证代码:
#include <iostream>
using namespace std;
class Qgw {
public:
virtual void Test() {
cout << "Test Position" << endl;
}
};
void Func() {
cout << "func" << endl;
}
int n = 10;
int main() {
Qgw* q = new Qgw;
q->Test();
const char* arr = "qgw";
cout << "text: " << (void*)Func << endl;
cout << ".rodata: " << (void*)arr << endl;
cout << "虚函数表: " << *(void**)q << endl;
cout << ".data: " << &n << endl;
return 0;
}
函数的地址在上文已经验证了,全局变量和常量字符串的位置,《深入理解计算机系统(第三版)》中说明如下:
.rodata
:只读数据 比如 printf 语句中的格式串(%d\n
) .data
:已初始化的全局和静态 C 变量
它们的布局如下图:
编译运行上述代码,得到以下结果:
可以看到虚函数表的位置在数据段和只读数据段位置之间,我们是无法手动修改虚函数表的,因此存储在只读数据段是比较合理的。
到此这篇关于详解C++虚函数表存储位置的文章就介绍到这了,更多相关C++虚函数表内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!