快乐虾
http://blog.csdn.net/lights_joy/
本文适用于
Xp sp3
Vs2008
欢迎转载,但请保留作者信息
近日,因为调试cygwin.dll出了点状况,与C++对象的内存布局有关,故此决定先看看vs2008对C++对象内存的管理。
写一小段简单代码:
class CTest
{
public:
int var_a;
int var_b;
int var_c;
public:
void fun1()
{
var_a = 10;
}
void fun2()
{
var_b = 20;
}
};
CTest a, *p;
int _tmain(int argc, _TCHAR* argv[])
{
a.var_a = 1;
a.var_b = 2;
a.var_c = 3;
p = &a;
p->fun1();
p->fun2();
return 0;
}
为了更直观地观察编译器的行为,把a和p做为全局变量。
先观察赋值语句的行为:
a.var_a = 1;
004113CE C7 05 C0 74 41 00 01 00 00 00 mov dword ptr [a (4174C0h)],1
a.var_b = 2;
004113D8 C7 05 C4 74 41 00 02 00 00 00 mov dword ptr [a+4 (4174C4h)],2
a.var_c = 3;
004113E2 C7 05 C8 74 41 00 03 00 00 00 mov dword ptr [a+8 (4174C8h)],3
再观察赋值结束后p指向的内存区域:
0x004174C0 01 00 00 00 02 00 00 00 03 00 00 00 00 00 00 ...............
显然,这个时候p指向CTest::var_a,也就是说vs2008并没有在类中插入多余的内容。CTest的大小为var_a, var_b, var_c的大小之和,即12个字节。
再观察一下函数调用的行为:
p->fun1();
004113F6 8B 0D BC 74 41 00 mov ecx,dword ptr [p (4174BCh)]
004113FC E8 53 FD FF FF call CTest::fun1 (411154h)
这段代码可以说明CTest::fun1的调用和其它的C代码并没有什么不同,一个函数而已。看看call把我们带到哪里:
00411118 E9 83 03 00 00 jmp CTest::fun2 (4114A0h)
0041111D E9 6E 15 00 00 jmp __CxxUnhandledExceptionFilter (412690h)
00411122 E9 E9 15 00 00 jmp __CxxSetUnhandledExceptionFilter (412710h)
00411127 E9 64 24 00 00 jmp QueryPerformanceCounter (413590h)
0041112C E9 9F 16 00 00 jmp _wsetargv (4127D0h)
00411131 E9 0A 17 00 00 jmp __p__commode (412840h)
00411136 E9 89 22 00 00 jmp _unlock (4133C4h)
0041113B E9 62 24 00 00 jmp GetCurrentProcessId (4135A2h)
00411140 E9 9B 04 00 00 jmp _RTC_CheckStackVars2 (4115E0h)
00411145 E9 80 18 00 00 jmp __set_app_type (4129CAh)
0041114A E9 A1 03 00 00 jmp _RTC_CheckEsp (4114F0h)
0041114F E9 8C 16 00 00 jmp _RTC_Initialize (4127E0h)
00411154 E9 07 03 00 00 jmp CTest::fun1 (411460h)
一个神秘的地方,只是func1和func2为什么要离得这么远呢?莫非有什么特殊的考虑?暂且不管它,在jmp的带领下,看到了func1的代码:
void fun1()
{
00411460 55 push ebp
00411461 8B EC mov ebp,esp
00411463 81 EC CC 00 00 00 sub esp,0CCh
00411469 53 push ebx
0041146A 56 push esi
0041146B 57 push edi
0041146C 51 push ecx
0041146D 8D BD 34 FF FF FF lea edi,[ebp-0CCh]
00411473 B9 33 00 00 00 mov ecx,33h
00411478 B8 CC CC CC CC mov eax,0CCCCCCCCh
0041147D F3 AB rep stos dword ptr es:[edi]
0041147F 59 pop ecx
00411480 89 4D F8 mov dword ptr [ebp-8],ecx
var_a = 10;
00411483 8B 45 F8 mov eax,dword ptr [this]
00411486 C7 00 0A 00 00 00 mov dword ptr [eax],0Ah
}
0041148C 5F pop edi
0041148D 5E pop esi
0041148E 5B pop ebx
0041148F 8B E5 mov esp,ebp
00411491 5D pop ebp
00411492 C3 ret
传说中的对象应该有一个this指针的,怎么传递过来的?
在调用fun1之前,将p的值赋给了ecx,在fun1里面,其它寄存器都是函数开始就入栈,函数退出时再出栈,唯独ecx是个例外,想来vs就是通过ECX来传递this指针的值。
由于在此种简单情况下,vs2008并没有添加额外的空间,想来把p指向自己定义的数组再调用其成员函数应该也可以工作得很好,试试下面的代码:
int buf[3] = {4, 5, 6};
p = (CTest*)buf;
p->fun1();
经过此调用,buf[0]的值果然变成了10。