- UID
- 2
- 精华
- 积分
- 7736
- 威望
- 点
- 宅币
- 个
- 贡献
- 次
- 宅之契约
- 份
- 最后登录
- 1970-1-1
- 在线时间
- 小时
|
下面仅以VS调试子功能——查看汇编代码进行分析,以VS2012源码为例:
- int main( )
- {
- 00E282E0 push ebp
- 00E282E1 mov ebp,esp
- 00E282E3 push 0FFFFFFFFh
- 00E282E5 push 0E29400h
- 00E282EA mov eax,dword ptr fs:[00000000h]
- 00E282F0 push eax
- 00E282F1 sub esp,220h
- 00E282F7 push ebx
- 00E282F8 push esi
- 00E282F9 push edi
- 00E282FA lea edi,[ebp-22Ch]
- 00E28300 mov ecx,88h
- 00E28305 mov eax,0CCCCCCCCh
- 00E2830A rep stos dword ptr es:[edi]
- 00E2830C mov eax,dword ptr ds:[00E300E0h]
- 00E28311 xor eax,ebp
- 00E28313 mov dword ptr [ebp-10h],eax
- 00E28316 push eax
- 00E28317 lea eax,[ebp-0Ch]
- 00E2831A mov dword ptr fs:[00000000h],eax
- MyClass* fPtr1 = new MyClass;
- 00E28320 push 4
- MyClass* fPtr1 = new MyClass;
- 00E28322 call operator new (0E21410h)
- 00E28327 add esp,4
- 00E2832A mov dword ptr [ebp-218h],eax
- 00E28330 mov dword ptr [ebp-4],0
- 00E28337 cmp dword ptr [ebp-218h],0
- 00E2833E je main+73h (0E28353h)
- 00E28340 mov ecx,dword ptr [ebp-218h]
- 00E28346 call MyClass::MyClass (0E210BEh)
- 00E2834B mov dword ptr [ebp-22Ch],eax
- 00E28351 jmp main+7Dh (0E2835Dh)
- 00E28353 mov dword ptr [ebp-22Ch],0
- 00E2835D mov eax,dword ptr [ebp-22Ch]
- 00E28363 mov dword ptr [ebp-224h],eax
- 00E28369 mov dword ptr [ebp-4],0FFFFFFFFh
- 00E28370 mov ecx,dword ptr [ebp-224h]
- 00E28376 mov dword ptr [fPtr1],ecx
- delete fPtr1;
- 00E28379 mov eax,dword ptr [fPtr1]
- 00E2837C mov dword ptr [ebp-200h],eax
- 00E28382 mov ecx,dword ptr [ebp-200h]
- 00E28388 mov dword ptr [ebp-20Ch],ecx
- 00E2838E cmp dword ptr [ebp-20Ch],0
- 00E28395 je main+0CCh (0E283ACh)
- 00E28397 push 1
- 00E28399 mov ecx,dword ptr [ebp-20Ch]
- 00E2839F call MyClass::`scalar deleting destructor' (0E2101Eh)
- 00E283A4 mov dword ptr [ebp-22Ch],eax
- 00E283AA jmp main+0D6h (0E283B6h)
- 00E283AC mov dword ptr [ebp-22Ch],0
- MyClass* fPtr2 = new( nothrow ) MyClass;
- 00E283B6 push 0E30228h
- 00E283BB push 4
- 00E283BD call operator new (0E210E1h)
- 00E283C2 add esp,8
- 00E283C5 mov dword ptr [ebp-1E8h],eax
- 00E283CB mov dword ptr [ebp-4],1
- 00E283D2 cmp dword ptr [ebp-1E8h],0
- 00E283D9 je main+10Eh (0E283EEh)
- 00E283DB mov ecx,dword ptr [ebp-1E8h]
- 00E283E1 call MyClass::MyClass (0E210BEh)
- 00E283E6 mov dword ptr [ebp-22Ch],eax
- 00E283EC jmp main+118h (0E283F8h)
- 00E283EE mov dword ptr [ebp-22Ch],0
- 00E283F8 mov eax,dword ptr [ebp-22Ch]
- 00E283FE mov dword ptr [ebp-1F4h],eax
- 00E28404 mov dword ptr [ebp-4],0FFFFFFFFh
- 00E2840B mov ecx,dword ptr [ebp-1F4h]
- 00E28411 mov dword ptr [fPtr2],ecx
- delete fPtr2;
- 00E28414 mov eax,dword ptr [fPtr2]
- 00E28417 mov dword ptr [ebp-1D0h],eax
- 00E2841D mov ecx,dword ptr [ebp-1D0h]
- 00E28423 mov dword ptr [ebp-1DCh],ecx
- 00E28429 cmp dword ptr [ebp-1DCh],0
- 00E28430 je main+167h (0E28447h)
- 00E28432 push 1
- 00E28434 mov ecx,dword ptr [ebp-1DCh]
- 00E2843A call MyClass::`scalar deleting destructor' (0E2101Eh)
- 00E2843F mov dword ptr [ebp-22Ch],eax
- 00E28445 jmp main+171h (0E28451h)
- 00E28447 mov dword ptr [ebp-22Ch],0
- char x1[sizeof( MyClass )];
- MyClass* fPtr3 = new( &x1[0] ) MyClass;
- 00E28451 mov eax,1
- 00E28456 imul eax,eax,0
- 00E28459 lea ecx,x1[eax]
- 00E2845D push ecx
- 00E2845E push 4
- 00E28460 call operator new (0E2105Ah)
- 00E28465 add esp,8
- 00E28468 mov dword ptr [ebp-1B8h],eax
- 00E2846E mov dword ptr [ebp-4],2
- 00E28475 cmp dword ptr [ebp-1B8h],0
- 00E2847C je main+1B1h (0E28491h)
- 00E2847E mov ecx,dword ptr [ebp-1B8h]
- 00E28484 call MyClass::MyClass (0E210BEh)
- 00E28489 mov dword ptr [ebp-22Ch],eax
- 00E2848F jmp main+1BBh (0E2849Bh)
- 00E28491 mov dword ptr [ebp-22Ch],0
- 00E2849B mov edx,dword ptr [ebp-22Ch]
- 00E284A1 mov dword ptr [ebp-1C4h],edx
- 00E284A7 mov dword ptr [ebp-4],0FFFFFFFFh
- 00E284AE mov eax,dword ptr [ebp-1C4h]
- 00E284B4 mov dword ptr [fPtr3],eax
- fPtr3 -> ~MyClass();
- 00E284B7 push 0
- 00E284B9 mov ecx,dword ptr [fPtr3]
- 00E284BC call MyClass::`scalar deleting destructor' (0E2101Eh)
-
- MyClass* fPtr4 = new MyClass[2];
- 00E284C1 push 0Ch
- 00E284C3 call operator new[] (0E2119Ah)
- 00E284C8 add esp,4
- 00E284CB mov dword ptr [ebp-1A0h],eax
- 00E284D1 mov dword ptr [ebp-4],3
- 00E284D8 cmp dword ptr [ebp-1A0h],0
- 00E284DF je main+23Bh (0E2851Bh)
- 00E284E1 mov eax,dword ptr [ebp-1A0h]
-
- MyClass* fPtr4 = new MyClass[2];
- 00E284E7 mov dword ptr [eax],2
- 00E284ED push 0E21023h
- 00E284F2 push 0E210BEh
- 00E284F7 push 2
- 00E284F9 push 4
- 00E284FB mov ecx,dword ptr [ebp-1A0h]
- 00E28501 add ecx,4
- 00E28504 push ecx
- 00E28505 call `eh vector constructor iterator' (0E21316h)
- 00E2850A mov edx,dword ptr [ebp-1A0h]
- 00E28510 add edx,4
- 00E28513 mov dword ptr [ebp-22Ch],edx
- 00E28519 jmp main+245h (0E28525h)
- 00E2851B mov dword ptr [ebp-22Ch],0
- 00E28525 mov eax,dword ptr [ebp-22Ch]
- 00E2852B mov dword ptr [ebp-1ACh],eax
- 00E28531 mov dword ptr [ebp-4],0FFFFFFFFh
- 00E28538 mov ecx,dword ptr [ebp-1ACh]
- 00E2853E mov dword ptr [fPtr4],ecx
- delete []fPtr4;
- 00E28541 mov eax,dword ptr [fPtr4]
- 00E28544 mov dword ptr [ebp-188h],eax
- 00E2854A mov ecx,dword ptr [ebp-188h]
- 00E28550 mov dword ptr [ebp-194h],ecx
- 00E28556 cmp dword ptr [ebp-194h],0
- 00E2855D je main+294h (0E28574h)
- 00E2855F push 3
- 00E28561 mov ecx,dword ptr [ebp-194h]
- 00E28567 call MyClass::`vector deleting destructor' (0E2128Ah)
- 00E2856C mov dword ptr [ebp-22Ch],eax
- 00E28572 jmp main+29Eh (0E2857Eh)
- 00E28574 mov dword ptr [ebp-22Ch],0
- MyClass* fPtr5 = new( nothrow ) MyClass[2];
- 00E2857E push 0E30228h
- 00E28583 push 0Ch
- 00E28585 call operator new[] (0E21311h)
- 00E2858A add esp,8
- 00E2858D mov dword ptr [ebp-170h],eax
- 00E28593 mov dword ptr [ebp-4],4
- 00E2859A cmp dword ptr [ebp-170h],0
- 00E285A1 je main+2FDh (0E285DDh)
- 00E285A3 mov eax,dword ptr [ebp-170h]
- 00E285A9 mov dword ptr [eax],2
- 00E285AF push 0E21023h
- 00E285B4 push 0E210BEh
- 00E285B9 push 2
- 00E285BB push 4
- 00E285BD mov ecx,dword ptr [ebp-170h]
- 00E285C3 add ecx,4
- 00E285C6 push ecx
- 00E285C7 call `eh vector constructor iterator' (0E21316h)
- 00E285CC mov edx,dword ptr [ebp-170h]
- 00E285D2 add edx,4
- 00E285D5 mov dword ptr [ebp-22Ch],edx
- 00E285DB jmp main+307h (0E285E7h)
- 00E285DD mov dword ptr [ebp-22Ch],0
- 00E285E7 mov eax,dword ptr [ebp-22Ch]
- 00E285ED mov dword ptr [ebp-17Ch],eax
- 00E285F3 mov dword ptr [ebp-4],0FFFFFFFFh
- 00E285FA mov ecx,dword ptr [ebp-17Ch]
- 00E28600 mov dword ptr [fPtr5],ecx
- delete []fPtr5;
- 00E28603 mov eax,dword ptr [fPtr5]
- 00E28606 mov dword ptr [ebp-158h],eax
- 00E2860C mov ecx,dword ptr [ebp-158h]
- 00E28612 mov dword ptr [ebp-164h],ecx
- 00E28618 cmp dword ptr [ebp-164h],0
- 00E2861F je main+356h (0E28636h)
- 00E28621 push 3
- 00E28623 mov ecx,dword ptr [ebp-164h]
- 00E28629 call MyClass::`vector deleting destructor' (0E2128Ah)
- 00E2862E mov dword ptr [ebp-22Ch],eax
- 00E28634 jmp main+360h (0E28640h)
- 00E28636 mov dword ptr [ebp-22Ch],0
- char x2[2*sizeof( MyClass ) + sizeof(int)];
- MyClass* fPtr6 = new ( &x2[0] ) MyClass[2];
- 00E28640 mov eax,1
- 00E28645 imul eax,eax,0
- 00E28648 lea ecx,x2[eax]
- 00E2864C push ecx
- 00E2864D push 0Ch
- 00E2864F call operator new[] (0E21389h)
- 00E28654 add esp,8
- 00E28657 mov dword ptr [ebp-140h],eax
- 00E2865D mov dword ptr [ebp-4],5
- 00E28664 cmp dword ptr [ebp-140h],0
- 00E2866B je main+3C7h (0E286A7h)
- 00E2866D mov edx,dword ptr [ebp-140h]
- 00E28673 mov dword ptr [edx],2
- 00E28679 push 0E21023h
- 00E2867E push 0E210BEh
- 00E28683 push 2
- 00E28685 push 4
- 00E28687 mov eax,dword ptr [ebp-140h]
- 00E2868D add eax,4
- 00E28690 push eax
- 00E28691 call `eh vector constructor iterator' (0E21316h)
- 00E28696 mov ecx,dword ptr [ebp-140h]
- 00E2869C add ecx,4
- 00E2869F mov dword ptr [ebp-22Ch],ecx
- 00E286A5 jmp main+3D1h (0E286B1h)
- 00E286A7 mov dword ptr [ebp-22Ch],0
- 00E286B1 mov edx,dword ptr [ebp-22Ch]
- 00E286B7 mov dword ptr [ebp-14Ch],edx
- 00E286BD mov dword ptr [ebp-4],0FFFFFFFFh
- 00E286C4 mov eax,dword ptr [ebp-14Ch]
- 00E286CA mov dword ptr [fPtr6],eax
- fPtr6[1].~MyClass();
- 00E286CD push 0
- 00E286CF mov ecx,4
- 00E286D4 shl ecx,0
- 00E286D7 add ecx,dword ptr [fPtr6]
- 00E286DA call MyClass::`scalar deleting destructor' (0E2101Eh)
- fPtr6[0].~MyClass();//按照和构造顺序相反顺序析构
- 00E286DF push 0
- 00E286E1 mov ecx,4
- 00E286E6 imul ecx,ecx,0
- 00E286E9 add ecx,dword ptr [fPtr6]
- 00E286EC call MyClass::`scalar deleting destructor' (0E2101Eh)
- }
- 00E286F1 xor eax,eax
- }
- 00E286F3 push edx
- 00E286F4 mov ecx,ebp
- 00E286F6 push eax
- 00E286F7 lea edx,ds:[0E28730h]
- 00E286FD call @_RTC_CheckStackVars@8 (0E21145h)
- 00E28702 pop eax
- 00E28703 pop edx
- 00E28704 mov ecx,dword ptr [ebp-0Ch]
- 00E28707 mov dword ptr fs:[0],ecx
- 00E2870E pop ecx
- 00E2870F pop edi
- 00E28710 pop esi
- 00E28711 pop ebx
- 00E28712 mov ecx,dword ptr [ebp-10h]
- 00E28715 xor ecx,ebp
- 00E28717 call @__security_check_cookie@4 (0E21050h)
- 00E2871C add esp,22Ch
- 00E28722 cmp ebp,esp
- 00E28724 call __RTC_CheckEsp (0E2135Ch)
- 00E28729 mov esp,ebp
- 00E2872B pop ebp
- 00E2872C ret
-
复制代码
第一种形式为常规new,MyClass* fPtr1 = new MyClass;实现方式:
单步步入后IDE定位到new.cpp的
- void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
- { // try to allocate size bytes
- void *p;
- while ((p = malloc(size)) == 0)
- if (_callnewh(size) == 0)
- { // report no memory
- _THROW_NCEE(_XSTD bad_alloc, );
- }
- return (p);
- }
复制代码
而步过之后直接跳转到构造函数中。
从上面new函数实现可以看到是使用malloc函数进行分配,如果失败则调用_callnewh调用注册过的“new操作符失败回调”函数(注册用_set_new_handler),如果原先没注册new失败回调,则抛出bad_alloc异常,可见在默认情况下,该while只会执行1次,仅当自定义new失败回调函数返回true,才可能多次尝试分配。
该种形式delete实现方式,单步以后vs不能定位到源码,不过我们可以换一种思路,既然知道一定执行析构函数,那么就在析构中下断点,断下后查看反汇编,并执行到上一级调用即可找到delete实现方法,因此看汇编实现,发现是一个名为“scalar deleting destructor”的内部函数:
- 00EB3470 push ebp
- 00EB3471 mov ebp,esp
- 00EB3473 sub esp,0CCh
- 00EB3479 push ebx
- 00EB347A push esi
- 00EB347B push edi
- 00EB347C push ecx
- 00EB347D lea edi,[ebp-0CCh]
- 00EB3483 mov ecx,33h
- 00EB3488 mov eax,0CCCCCCCCh
- 00EB348D rep stos dword ptr es:[edi]
- 00EB348F pop ecx //以上部分为debug版API常见头,无需理会
- 00EB3490 mov dword ptr [this],ecx
- 00EB3493 mov ecx,dword ptr [this]
- 00EB3496 call MyClass::~MyClass (0EB1023h) //执行析构
- 00EB349B mov eax,dword ptr [ebp+8]
- 00EB349E and eax,1
- 00EB34A1 je MyClass::`scalar deleting destructor'+3Fh (0EB34AFh) //如果传入参数允许释放则进行调用对应delete函数(对于定位new对应的delete该参数是设置为不允许的)
- 00EB34A3 mov eax,dword ptr [this]
- 00EB34A6 push eax
- 00EB34A7 call operator delete (0EB1154h)
- 00EB34AC add esp,4
- 00EB34AF mov eax,dword ptr [this] //以下是无关的收尾工作
- 00EB34B2 pop edi
- 00EB34B3 pop esi
- 00EB34B4 pop ebx
- 00EB34B5 add esp,0CCh
- 00EB34BB cmp ebp,esp
- 00EB34BD call __RTC_CheckEsp (0EB1352h)
- 00EB34C2 mov esp,ebp
- 00EB34C4 pop ebp
- 00EB34C5 ret 4
-
复制代码
执行到call operator delete这行,步入之后转到源码,可以看到使用的是dbgdel.cpp的delete函数。实现如下:
- void operator delete( void *pUserData )
- {
- _CrtMemBlockHeader * pHead;
- RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
- if (pUserData == NULL)
- return;
- _mlock(_HEAP_LOCK); /* 阻塞其他线程*/
- __TRY
- /* 得到用于内存块信息头指针*/
- pHead = pHdr(pUserData);
- /* 检查区块类型 */
- _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
- _free_dbg( pUserData, pHead->nBlockUse );//调用free函数释放内存
- __FINALLY
- _munlock(_HEAP_LOCK); /* 解锁其他线程*/
- __END_TRY_FINALLY
- return;
- }
复制代码
第二种方式为不抛出异常的new,MyClass* fPtr2 = new( nothrow ) MyClass;实现方式:
单步到newopnt.cpp中,
- void * __CRTDECL operator new(size_t count, const std::nothrow_t&) _THROW0()
- { // try to allocate count bytes
- void *p;
- _TRY_BEGIN
- p = operator new(count);
- _CATCH_ALL
- p = 0;
- _CATCH_END
- return (p);
- }
- #define _TRY_BEGIN try {
- #define _CATCH(x) } catch (x) {
- #define _CATCH_ALL } catch (...) {
- #define _CATCH_END }
复制代码
可见,这里用try块捕获了异常,因此不再抛出异常,余下的就是调用常规new函数而已。
第三种方式为定位new,
char x1[sizeof( MyClass )];
MyClass* fPtr3 = new( &x1[0] ) MyClass;
这里所谓的定位就是说告诉new我们已经有一个内存位置了:
单步后发现new位于new文件:
- inline void *__CRTDECL operator new(size_t, void *_Where) _THROW0()
- { // construct array with placement at _Where
- return (_Where);
- }
复制代码
可以发现该new什么都没做,那么为什么还要new呢?仔细想想可以知道,编译器对new的处理是调用new函数后,之后将该地址作为this指针进行初始化操作(比如设置虚表),再调用构造函数,而构造函数这玩意不能直接调用,不像析构函数那样,因为构造之前还没有对象和指针呢,对象和指针是构造以后才有的,而调用析构函数的时候,是已经有对象或指针的。所以这种定位new在我理解,就是可以相当于可以直接构造了。
从上面可以看到new操作符先调用合适的new函数分配空间,之后调用构造函数构造,而delete函数刚好相对,先进行析构之后调用析构函数析构;同时可以看到布局new操作符的好处是可以手动指定构造和析构的时间,对于new无论哪种形式,在调用new函数分配好内存后都会调用构造函数进行构造,而定位new函数实则是直接返回,这就导致直接使用当前地址进行构造,相当于显示调用构造函数,而析构时由于没有实际分配空间,因此不能用delete,而是显示调用析构函数进行析构。
上面都是对于有构造函数和析构函数对象的情况,用delete时,编译器会为该类专门生成一个scalar deleting destructor函数,该函数中先进行析构,之后调用operator delete函数。当然,如果没有析构函数,那么就不会有scalar deleting destructor函数了,此时单步是可以看到delete源码的,即dbgdel.cpp中的void operator delete(void *pUserData)函数。这一点在delete用于基本类型时显而易见。
下面再来看数组的情况
第一种类型new,MyClass* fPtr4 = new MyClass[2],单步以后定位到newaop.cpp中:
- void *__CRTDECL operator new[](size_t count) _THROW1(std::bad_alloc)
- { // try to allocate count bytes for an array
- return (operator new(count));
- }
复制代码
而编译器传给该new[]函数的参数count是sizeof(MyClass[2])+sizeof(int) 该sizeof(int)用于内存管理
new[]()仍然调用了new();没有本质区别,即这么多对象占用的内存是当作整体分配的。再分配好之后,就需要对每个对象this指针处进行初始化和构造了。
这些都是边研究边写的,剩下2种情况下次讲述 |
|