Visual Studio调试技巧
我们使用Visual Studio工具来查看C++对象的内存布局,所以需要在当前项目上右键单击选择“属性”后,打开属性页,在配置属性->C/C+±>命令行下的其它选项文本框中配置如下命令:
/d1 reportAllClassLayout
在“调试”中选“输出”,然后在下来框中选“生成”
无虚函数
/*
Base1.cpp
*/
class Base1
{
public:
int base1_1;
int base1_2;
void base1_fun1() {
}
};
内存分布只有base1_1和base1_2,base1_fun1()函数的内存是在调用时动态分配
此种情况下,内存分布为
-->+------------+
| base1_1 |
+------------+
| base1_2 |
+------------+
单个类,没继承
/*
Base1.cpp
*/
class Base1
{
public:
int base1_1;
int base1_2;
virtual void base1_fun1() {}
virtual void base1_fun2() {}
};
/*
main.cpp
*/
#include "Base1.cpp"
int main()
{
Base1* b1 = new Base1();
b1->base1_fun1();
return 0;
}
反汇编之后的结果
b1->base1_fun1();
007219D7 mov eax,dword ptr [b1] //把b1地址的值赋值给eax
007219DA mov edx,dword ptr [eax] //把eax对应的内存地址上的值赋值给edx,即edx内保存b1所对内存的地址
007219DC mov esi,esp
007219DE mov ecx,dword ptr [b1] //b1对应内存的地址赋值给ecx
007219E1 mov eax,dword ptr [edx] //edx对应内存的地址,即b1指针赋值给eax
007219E3 call eax //调用eax
总的来说,就是b1的首地址给eax,call eax就是调用base1_fun1()
结合vs2019上的变量监视以及类对象内存结构分析可知,b1的首地址保存的是虚函数表(__vfptr)
__vfptr的类型是void**,因此,指向的是一个数组,该数组的内部是 void * 对象,即
__vfprt-->+--------+
| void * |
+--------+
| void * |
+--------+
根据上述的代码可知
__vfptr --> +--------------+
| base1_func1()|
+--------------+
GDB调试
为了方便,将上面的堆分配改为栈分配
/*
main.cpp
*/
#include "Base1.cpp"
int main()
{
Base1 b1;
b1.base1_fun1();
return 0;
}
首先用g++进行编译
g++ base.h main.cpp -g
然后用gdb进行调试
gdb ./a.out
b main
将断点停在b1.base1_func1()
然后用gdb查看内存变化
可以看一下ZTV4Base是什么
用