详解C++虚函数表存储位置

 更新时间:2023年04月03日 11:12:38   作者:叫我小秦就好了  
相信大家知道虚表指针和虚函数存储的位置,但对于虚函数表的存储位置一时无法确定。本文就来和大家详细聊聊相关内容,希望对大家有所帮助

前言

先说结论:虚函数表存储在只读数据段.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++虚函数表内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++基于boost asio实现sync tcp server通信流程详解

    C++基于boost asio实现sync tcp server通信流程详解

    这篇文章主要介绍了C++基于boost asio实现sync tcp server通信的流程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • Visual Studio Code (VSCode) 配置搭建 C/C++ 开发编译环境的流程

    Visual Studio Code (VSCode) 配置搭建 C/C++ 开发编译环境的流程

    记得N年前刚开始接触编程时,使用的是Visual C++6.0,下面这个可爱的图标很多人一定很熟悉。不过今天想尝鲜新的工具 Visual Studio Code 来搭建C/C++开发环境,感兴趣的朋友一起看看吧
    2021-09-09
  • C语言实现电话簿项目

    C语言实现电话簿项目

    这篇文章主要为大家详细介绍了C语言实现电话簿项目,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-12-12
  • 深入浅析C/C++ 的条件编译

    深入浅析C/C++ 的条件编译

    条件编译是指预处理的时候根据条件编译的指令有条件的选择源程序中的一部分代码送给编译器进行编译,进行有选择性的操作,防止宏替换的内容重复包含,这篇文章主要介绍了C/C++ 的条件编译,需要的朋友可以参考下
    2022-04-04
  • C语言实现三子棋游戏含完整代码

    C语言实现三子棋游戏含完整代码

    本文详细讲解了C语言实现三子棋游戏内含完整代码,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-11-11
  • C++关于const与引用的分析讲解

    C++关于const与引用的分析讲解

    Const 是C++中常用的类型修饰符,常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的,引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量
    2022-04-04
  • C++:构造函数,析构函数详解

    C++:构造函数,析构函数详解

    今天小编就为大家分享一篇关于C++构造函数和析构函数的文章,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2021-09-09
  • 内核线程优先级设置的方法介绍

    内核线程优先级设置的方法介绍

    本篇文章介绍了,内核线程优先级设置的方法。需要的朋友参考下
    2013-05-05
  • 利用C/C++实现贪吃蛇游戏

    利用C/C++实现贪吃蛇游戏

    这篇文章主要为大家详细介绍了利用C/C++实现贪吃蛇游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-10-10
  • Ubuntu18.04下QT开发Android无法连接设备问题解决实现

    Ubuntu18.04下QT开发Android无法连接设备问题解决实现

    本文主要介绍了Ubuntu18.04下QT开发Android无法连接设备问题解决实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06

最新评论