此篇幅主要研究简单类内存结构,用于学习反汇编
class CClassTest
{
public:
CClassTest();
~CClassTest();
public:
virtual void _stdcall SetAge(const int age);
virtual int GetAge();
void _stdcall Geive();
void static StaticFun(int staticParam);
private:
void _stdcall SSk();
private:
int m_age;
int m_session;
static int m_global;
};
int CClassTest::m_global = 12;
CClassTest::CClassTest()
{
m_age = (int)GetTickCount();
m_session = 10;
}
CClassTest::~CClassTest()
{
}
int CClassTest::GetAge()
{
return m_age;
}
void CClassTest::SetAge(const int age)
{
m_age += 56;
m_session = 89;
SSk();
}
void CClassTest::SSk()
{
m_age = 59;
m_session += m_age;
CClassTest::m_global += 98;
}
void _stdcall CClassTest::Geive()
{
m_session = m_age;
}
void CClassTest::StaticFun(int staticParam)
{
printf("CClassTest::StaticFun param=%d\r\n", staticParam);
}
//主程序调用部分
CClassTest *w = new CClassTest();
int _tmain(int argc, _TCHAR* argv[])
{
//printf("w=%x size=%d\r\n", w, sizeof(*w));
//普通函数
int res = w->GetAge();
printf("age=%d\r\n", res);
//虚函数
w->SetAge(argc);
w->Geive();
//静态函数
CClassTest::StaticFun(156);
return 0;
}
这个类,我们定义的非常简单,主要用于研究其内存结构,代码没有任何意义,同时编译时关闭优化,不然这个类转为汇编后将面目全非。
1、类是怎么分布的
2、程序变量和函数怎么分布
3、静态成员怎么分布
4、调用类函数汇编是什么样的
首先看看MAIN函数:
可以看到头两行是例行保存SP,然后保存cx,
调用GetAge虚函数,图中1位置
开始正式工作,首先将[w]放入ax(w是一个指针,他的值就是一个地址,这里就是this指针),然后把[ax]的值(就是函数的虚表地址)放入dx,然后[dx+4]就是函数GetAge的地址(这个函数在类中是第二虚函数,vptr+0=SetAge, vptr+4=GetAge),我看的在调用GetAge之前,我们将this指针放入了cx中,便于在GetAge函数中使用,(注意,我发现只有在调用对象的第一个函数的时候才会这样传值,而在其后调用SetAge时,this指针式被当做参数push进去的,而且永远是第一个参数,也就是最后一个入栈的)
我们再看GetAge内容
函数定义的时候采用了GetAge(CClassTest *this)的方式,但是函数内部并没有使用到这个参数,而是重新定义了一个局部变量来存放this指针, 将cx入栈,这时候[bp-4]就是刚刚入栈的cx的位置,将cx的值放入其中,然后ax中存放的是对象的起始地址,偏移4字节,就是跳过虚函数指针变量(因为这个类带有虚函数),刚好是m_age变量的地址,[ax+4]放入ax函数调用结束
继续看=SetAge函数,就是图中2的部分,有两个push第一个是age参数,第二个是this指针,
SetAge函数体:
m_age是this偏移4字节,m_session是this偏移8字节,这样看代码应该很清楚了,然后是调用SSK()函数,可以看到,直接this指针入栈,然后调用,等价于 SSk(CClassTest* this);
下面看普通函数Geive调用(不占用类内存),图中3
首先是push了this指针,然后直接调用了一个函数,等价于调用了 Geive(CClassTest* this);
图说明了一切。
下面是类静态函数调用(不占用类内存),图中4位置:
调用之前将staticParam入栈了