这篇文章主要介绍“C++虚函数表存储位置在哪”,在日常操作中,相信很多人在C++虚函数表存储位置在哪问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++虚函数表存储位置在哪”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
前言
先说结论:虚函数表存储在只读数据段(
.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)
.data:已初始化的全局和静态 C 变量
它们的布局如下图:
编译运行上述代码,得到以下结果:
可以看到虚函数表的位置在数据段和只读数据段位置之间,我们是无法手动修改虚函数表的,因此存储在只读数据段是比较合理的。