C++类的内存分布
1.非静态成员变量,虚函数,内存对齐会影响类内存大小.本文优先考虑64位程序
A 类结构:
class A
{
• virtual void Func(){};
• int m_X;
• short m_Y;
}
32位下 sizeof(A)==12字节
64位下 sizeof(A)==16字节
实例化内存布局:
32位下:
A:{
• 0-4 :虚函数表
• 4-8: m_X 成员变量
• 8-12: m_Y 成员变量 (内存对齐)
}
64位下:
A:{
• 0-8 :虚函数表
• 8-12: m_X 成员变量
• 12-16: m_Y 成员变量 (内存对齐)
}
1.如果类里面有虚函数,类前4个字节是个虚函数表的指针.(64位是前8字节)。
2.会和结构体一样,存在内存对齐行为(这里就不展开了)。
3.类的成员函数(非虚函数,虚函数会放在虚表里)不会占用类的内存。
2.当子类继承多个有虚函数的父类
说明:会有多个虚表,比声明成员类少一个。
1.例子中A和C中都有虚函数,B继承了A和C,B的内存中就有两个虚函数表;
2.内存布局和声明的顺序有对应关系,具体内存排列顺序请看 1的内容。
思考题:C类自己的虚表去了哪里?
公布答案:
1自己的虚表还是首地址的地方,它和第一个虚表融合到了一起。(请看下面图片讲解)
1:B类的原型
2:C类的原型
3:C类内存结构,红色是他继承的 B A D 类,绿色是自己成员x内存。
4:查看下第一个也就是 0x000000014001C9A8地址。
你会发现原本B类里面只有三个虚函数此时内存中有4个虚函数,讲解:红色方框内是被C类重写的函数,绿色方框是B类自己两个虚函数(因为不重名所以没被重写),而最后一个蓝色方框是C类自己的boo虚函数。
5:其余两个类 A和D的虚表中只有三个函数(A和D函数同名只是打印内容不同)没有蓝色方块的函数,这里就不贴图了,我相信你们能理解。~.~
6:以上5条都是以继承的形式进行讨论的。别的形式就不准确了。
3.不以继承的形式使用其他类
说明:以成员变量的形式,会有多个虚表,比继承多一个。
C类:
1.第一个是自己,接下来依次声明排列。与继承内存布局不同。
4.std::type_infor逆向查找验证
在C++ 类中存在虚函数的情况下 : 类中虚表的地址减去一个指针的大小取值,然后值再加上12字节取出就是std::type_infor对象地址指针。
此图是32位C++类的结构:
不过在64位下放的就不是指针而是一个偏移。
我目前测试环境是vs2022下,别的编译器就不清楚了.
1.取出虚表地址 0x000000014001c9a8。
2.虚表地址减去void*大小(64位下8字节)也就是箭头指向地址。
3.取出箭头地址转到,再加上12字节也就是方框中的数据(0x0001f3c8)。
4.0x140000000(模块基址)+0x0001f3c8。
5.红色方框就是std::type_infor里面的数据
0x000000014001d0c8=0x000000014001c9a8/*虚表首地址*/-sizeof(void*);
0x0001f3c8=*(DWORD)((QWORD)0x000000014001d0c8+12)
0x000000014001F3C8=0x140000000/*进程模块基址*/ +0x0001f3c8;
6.执行代码验证,学过汇编的同学都知道RAX一般情况下是用来存放函数返回值,本文中是(0x00000014001F3C8)。
7.正向代码验证。
C++ 类中无虚表(无虚函数)的情况下情况是不一样的如下.
1.上图是有虚表的情况,调用了__RTtypeid函数其功能就是 本文中第四条所讲内容。
2.没有虚表的情况,他将会直接std::type_infor对象地址指针赋值给目标。
5.std::type_infor 介绍
1.x64下std::type_infor大小固定为24字节。
2.+0 偏移:存放着 type_infor 的虚函数表,里面有一个虚函数 为type_info::`scalar deleting destructor'(unsigned int)。
3.+8 偏移:_UndecoratedName = 0x0000000000000000 <NULL>,如图:
4.+0x10 偏移:是他的“类名”,这个“类名”是需要处理才是真正的类名,去掉开头6个字节和结尾3个字节。
5.至此type_infor 解析完毕。