アセンブリから見るc++のnewとdelete
44804 ワード
次はc++ソースです.
コードは簡単で、main関数でnewでスタックオブジェクトを構築し、deelteでオブジェクトを解放します.
次に、構築スタックオブジェクトのアセンブリコードを見てみましょう.
符号化から分かるように,newでスタックオブジェクトを構築する際の大まかな流れは,次のようになる.
1 operator new申請スタックスペースを呼び出す
2申請したスタックスペースの先頭アドレスを検査し、申請の失敗を防止し、空のポインタを返す
3スタック空間の申請が成功した場合、構造関数を呼び出し、スタック空間のヘッダアドレスをxpポインタに渡す.
従って,c++でnewでスタック空間を申請するのはmallocで申請するのとは異なり,前者はスタック空間が申請に成功したかどうかを自動的に検出する.
次に、プロファイル・スタック・オブジェクトの符号化を参照します.
アセンブリ符号化から,呼び出しdeleteはoperator newのように解析関数とoperator delete関数を直接呼び出すのではなく,operator deleteの機能を完了するために解析エージェント関数を呼び出すことが分かる.プロキシ関数を使用するのは、解放するオブジェクトが1つ以上ある場合があります.
次に、プロファイルエージェント関数の符号化を示します.
アセンブリ符号化から,プロファイルエージェント関数はまず真のプロファイル関数を呼び出し,delete関数を呼び出して申請したスタック空間を解放することが分かる.この中間には、オブジェクトタイプフラグによりoperator deleteを呼び出してスタック空間を解放するか否かを判断する判断プロセスがある.
上記の符号化によりdeleteの流れは大体次のように見えます.
1オブジェクトヘッダ値が空かどうかを検出する
2空でない場合は、プロファイルエージェント関数を呼び出します.そうでない場合は、プロファイルエージェント関数を呼び出しません.
3解析エージェント関数でオブジェクトの解析関数とdelete関数を呼び出します(オブジェクトフラグが0でない場合)
c++のdelete呼び出しプロセスから見ると、deleteはポインタ変数xpの値を自動的にゼロにすることはありません.したがって、後続のプログラムではxpポインタを使用しても実行可能である.ポインタ変数xpとxpが指すオブジェクトの最大の違いは、どの宣言が終了したかです.deleteを呼び出すと、xpが指すオブジェクトは解析され、非合法になりますが、アドレス自体は合法的なプログラム空間を表します.
オブジェクト配列
次にnewとdeleteがオブジェクト配列に適用される場合を見る.
c++ソースコードは以下の通りです.
上記のコードはnewでスタックに2つのオブジェクトを構築しdeleteで解放する.
次に、構造プロセスの符号化を見ます.
上記の符号化プロセスは、概ね次のとおりです.
1 operator newを呼び出してスタックスペースを割り当てる
2コンストラクションエージェント関数コンストラクションスタックオブジェクトを呼び出し、コンストラクションエージェント関数を呼び出すと、スタックを介して5つのパラメータが渡され、それぞれa)最初のスタックオブジェクトヘッダb)スタックオブジェクトサイズ
c)スタックオブジェクト個数d)コンストラクション関数アドレスe)コンストラクション関数アドレス
3第1のスタックオブジェクトの先頭アドレスを返します.申請されたスタックスペースの先頭アドレスではありません.
上記の符号化からも分かるように、申請されたスタック空間ヘッダアドレスは、オブジェクトの個数を格納するために使用されるので、スタック空間の合計サイズは、オブジェクトサイズ*オブジェクト個数ではない
以下は、エージェント関数を構築するための集計符号化です.関連する部分のみを参照してください.
プロキシ関数を構築する役割は、コンストラクション関数をループ呼び出し、配列内の各オブジェクトを順次構築することです.
次に、配列オブジェクトを解放するための集計コードを示します.
vector deleting destructor関数の符号化:
vector deleting destructorの全体的な流れも、まずプロファイルエージェント関数を呼び出し、operator delete解放空間(オブジェクトフラグが0でない場合)を呼び出し、プロファイルエージェント関数を呼び出すときも4つのパラメータaを呼び出す)スタック空間の最初のオブジェクトヘッダアドレスb)対象サイズc)オブジェクト個数d)虚関数アドレス
次に、プロファイル・エージェント関数の集計符号化を示します.
解析エージェント関数も、構築オブジェクトとは逆の順序でオブジェクトを解析するために、解析関数をループ呼び出していることがわかります.
単一のスタックオブジェクトに対してdeleteを呼び出す場合とスタックオブジェクト配列に対してdeleteを呼び出す場合とを比較すると、2つの場合の最も主要な違いは、最後にdeleteでスタック空間を解放する場合、delete[]がターゲットポインタ(すなわちdeleteにとって、ターゲットポインタはスタックオブジェクトヘッダアドレスであり、delete[]にとってスタック内の最初のスタックオブジェクトヘッダアドレスである)に対して4を減らす調整を行うことである.したがって,単一オブジェクトスタック空間を解放しdelete[]を誤って使用すると,中間検出を実行すると,オブジェクトタイプが3とマークされ,ターゲットポインタの調整が行われ,結果として誤ったスタック空間が解放される.同様に,オブジェクト配列を解放しdeletを誤って使用すると,中間検出を実行すると,オブジェクトタイプが1と判定され,ターゲットポインタ調整を行わずにスタック空間の解放にもエラーが発生する.
基本データ型
次はc++ソースです.
c++コードのip 1は、スタック内の基本タイプint配列のヘッダアドレスを指し、ip 1は、スタック内の単一の基本タイプintのヘッダアドレスを指す.
次はmian関数の符号化です.
符号化から,基本データ型には構造関数と構造関数がないため,この2つの場合は単純にnew割り当て空間を呼び出しdelete解放空間を呼び出すだけであることがわかる.また、スタック内のオブジェクト配列とは異なり、スタック内の基本タイプ配列が申請されたスタック空間ヘッダにオブジェクト個数を格納していないことも見られ、ip 1,ip 2はそれぞれ申請されたスタック空間ヘッダを直接指している.そのため、基本タイプではdeleteとdelete[]の効果は同じである.
class X {
private:
int _x;
public:
X(int xx = 0) : _x(xx) {}
~X() {}
};
int main() {
X* xp = new X;
delete xp;
}
コードは簡単で、main関数でnewでスタックオブジェクトを構築し、deelteでオブジェクトを解放します.
次に、構築スタックオブジェクトのアセンブリコードを見てみましょう.
: X* xp = new X;
push 4; 4byte, operator new
call ??2@YAPAXI@Z ; operator new
add esp, 4;operator new , 4byte, operator new
mov DWORD PTR $T2579[ebp], eax; eax , ST2579
cmp DWORD PTR $T2579[ebp], 0; ST2579 0 , NULL
je SHORT $LN3@main; , $LN3@main , 。
push 0; 0 ,
mov ecx, DWORD PTR $T2579[ebp]; ST2579 ( ) ecx,
; this
call ??0X@@QAE@H@Z ;
mov DWORD PTR tv70[ebp], eax; , eax , tv70
jmp SHORT $LN4@main; $LN4@main
$LN3@main:
mov DWORD PTR tv70[ebp], 0; , 0 tv70
$LN4@main:
mov eax, DWORD PTR tv70[ebp]; tv70 eax
mov DWORD PTR _xp$[ebp], eax; eax xp
符号化から分かるように,newでスタックオブジェクトを構築する際の大まかな流れは,次のようになる.
1 operator new申請スタックスペースを呼び出す
2申請したスタックスペースの先頭アドレスを検査し、申請の失敗を防止し、空のポインタを返す
3スタック空間の申請が成功した場合、構造関数を呼び出し、スタック空間のヘッダアドレスをxpポインタに渡す.
従って,c++でnewでスタック空間を申請するのはmallocで申請するのとは異なり,前者はスタック空間が申請に成功したかどうかを自動的に検出する.
次に、プロファイル・スタック・オブジェクトの符号化を参照します.
: delete xp;
mov ecx, DWORD PTR _xp$[ebp]; xp ( ) ecx
mov DWORD PTR $T2591[ebp], ecx; ecx ST2591
mov edx, DWORD PTR $T2591[ebp]; ST2591 edx
mov DWORD PTR $T2590[ebp], edx; edx ST2590
cmp DWORD PTR $T2590[ebp], 0; ST2590 0 ,
je SHORT $LN5@main; , $LN5@main , , 。
push 1; 1 3 0 , ( )
mov ecx, DWORD PTR $T2590[ebp]; ST2590 ecx, (this )
call ??_GX@@QAEPAXI@Z;
mov DWORD PTR tv75[ebp], eax; , eax , eax tv75
jmp SHORT $LN1@main; $LN1@main
$LN5@main:
mov DWORD PTR tv75[ebp], 0; , tv75 0
$LN1@main:; main
アセンブリ符号化から,呼び出しdeleteはoperator newのように解析関数とoperator delete関数を直接呼び出すのではなく,operator deleteの機能を完了するために解析エージェント関数を呼び出すことが分かる.プロキシ関数を使用するのは、解放するオブジェクトが1つ以上ある場合があります.
次に、プロファイルエージェント関数の符号化を示します.
??_GX@@QAEPAXI@Z PROC ; X::`scalar deleting destructor', COMDAT
; _this$ = ecx
push ebp
mov ebp, esp
push ecx; ecx ,
mov DWORD PTR _this$[ebp], ecx; ecx
mov ecx, DWORD PTR _this$[ebp]; ecx,
call ??1X@@QAE@XZ ;
mov eax, DWORD PTR ___flags$[ebp]; , , eax
and eax, 1; eax ( ) 1 delete
je SHORT $LN1@scalar; 0, $LN1@scalar , , ,
mov ecx, DWORD PTR _this$[ebp]; ecx
push ecx; ecx , delete
call ??3@YAXPAX@Z ; delete
add esp, 4;delete , 4byte, delete
$LN1@scalar:
mov eax, DWORD PTR _this$[ebp]; eax
;
mov esp, ebp
pop ebp
ret 4
??_GX@@QAEPAXI@Z ENDP
アセンブリ符号化から,プロファイルエージェント関数はまず真のプロファイル関数を呼び出し,delete関数を呼び出して申請したスタック空間を解放することが分かる.この中間には、オブジェクトタイプフラグによりoperator deleteを呼び出してスタック空間を解放するか否かを判断する判断プロセスがある.
上記の符号化によりdeleteの流れは大体次のように見えます.
1オブジェクトヘッダ値が空かどうかを検出する
2空でない場合は、プロファイルエージェント関数を呼び出します.そうでない場合は、プロファイルエージェント関数を呼び出しません.
3解析エージェント関数でオブジェクトの解析関数とdelete関数を呼び出します(オブジェクトフラグが0でない場合)
c++のdelete呼び出しプロセスから見ると、deleteはポインタ変数xpの値を自動的にゼロにすることはありません.したがって、後続のプログラムではxpポインタを使用しても実行可能である.ポインタ変数xpとxpが指すオブジェクトの最大の違いは、どの宣言が終了したかです.deleteを呼び出すと、xpが指すオブジェクトは解析され、非合法になりますが、アドレス自体は合法的なプログラム空間を表します.
オブジェクト配列
次にnewとdeleteがオブジェクト配列に適用される場合を見る.
c++ソースコードは以下の通りです.
class X {
private:
int _x;
public:
X(int xx = 0) : _x(xx) {}
~X() {}
};
int main() {
X* xp = new X[2];
delete [] xp;
}
上記のコードはnewでスタックに2つのオブジェクトを構築しdeleteで解放する.
次に、構造プロセスの符号化を見ます.
X* xp = new X[2];
00F035BD push 0Ch ; operator new ,
; 4byte, 12byte,
; 4byte
00F035BF call operator new ; operator new
00F035C4 add esp,4 ;operator new , 4byte, operator new
00F035C7 mov dword ptr [ebp-0F8h],eax; eax
00F035CD mov dword ptr [ebp-4],0 ;
00F035D4 cmp dword ptr [ebp-0F8h],0 ; 0 ,
00F035DB je main+97h (0F03617h) ; , NULL, 0F03617h ,
00F035DD mov eax,dword ptr [ebp-0F8h] ; eax
00F035E3 mov dword ptr [eax],2 ; 2 , 4byte
00F035E9 push offset X::~X (0F011DBh) ; ,
00F035EE push offset X::`default constructor closure' ; ,
00F035F3 push 2 ; ,
00F035F5 push 4 ; ,
00F035F7 mov ecx,dword ptr [ebp-0F8h] ; ecx
00F035FD add ecx,4 ;ecx 4 4byte, ecx
00F03600 push ecx ; ecx,
00F03601 call `eh vector constructor iterator' (0F011E5h) ;
00F03606 mov edx,dword ptr [ebp-0F8h] ; edx
00F0360C add edx,4 ;edx 4 4byte, edx
00F0360F mov dword ptr [ebp-10Ch],edx
00F03615 jmp main+0A1h (0F03621h) ; 0F03621h
00F03617 mov dword ptr [ebp-10Ch],0 ; ,
00F03621 mov eax,dword ptr [ebp-10Ch]
00F03627 mov dword ptr [ebp-104h],eax
00F0362D mov dword ptr [ebp-4],0FFFFFFFFh
00F03634 mov ecx,dword ptr [ebp-104h]
00F0363A mov dword ptr [ebp-14h],ecx ; xp
上記の符号化プロセスは、概ね次のとおりです.
1 operator newを呼び出してスタックスペースを割り当てる
2コンストラクションエージェント関数コンストラクションスタックオブジェクトを呼び出し、コンストラクションエージェント関数を呼び出すと、スタックを介して5つのパラメータが渡され、それぞれa)最初のスタックオブジェクトヘッダb)スタックオブジェクトサイズ
c)スタックオブジェクト個数d)コンストラクション関数アドレスe)コンストラクション関数アドレス
3第1のスタックオブジェクトの先頭アドレスを返します.申請されたスタックスペースの先頭アドレスではありません.
上記の符号化からも分かるように、申請されたスタック空間ヘッダアドレスは、オブジェクトの個数を格納するために使用されるので、スタック空間の合計サイズは、オブジェクトサイズ*オブジェクト個数ではない
以下は、エージェント関数を構築するための集計符号化です.関連する部分のみを参照してください.
00F03792 mov dword ptr [ebp-20h],0
00F03799 mov dword ptr [ebp-4],0
00F037A0 mov dword ptr [ebp-1Ch],0 ; 0 for , 0
00F037A7 jmp `eh vector constructor iterator'+52h (0F037B2h) ; 0F037B2h
00F037A9 mov eax,dword ptr [ebp-1Ch] ; eax
00F037AC add eax,1 ; eax 1, 1
00F037AF mov dword ptr [ebp-1Ch],eax ;
00F037B2 mov ecx,dword ptr [ebp-1Ch] ; ecx
00F037B5 cmp ecx,dword ptr [ebp+10h]; ecx dword ptr [ebp+10h] ( 2) ,
00F037B8 jge `eh vector constructor iterator'+6Bh (0F037CBh) ; 2, 0F037CBh ,
00F037BA mov ecx,dword ptr [ebp+8] ; ecx,
00F037BD call dword ptr [ebp+14h] ;
00F037C0 mov edx,dword ptr [ebp+8] ; edx
00F037C3 add edx,dword ptr [ebp+0Ch] ;edx , ,
00F037C6 mov dword ptr [ebp+8],edx ;
00F037C9 jmp `eh vector constructor iterator'+49h (0F037A9h); 0F037A9h
;====================== , , ========================
プロキシ関数を構築する役割は、コンストラクション関数をループ呼び出し、配列内の各オブジェクトを順次構築することです.
次に、配列オブジェクトを解放するための集計コードを示します.
delete [] xp;
00A2363D mov eax,dword ptr [ebp-14h]; eax
; eax
00A23640 mov dword ptr [ebp-0E0h],eax
00A23646 mov ecx,dword ptr [ebp-0E0h]
00A2364C mov dword ptr [ebp-0ECh],ecx ; eax ( ) ebp-0ECh
00A23652 cmp dword ptr [ebp-0ECh],0 ;
00A23659 je main+0F0h (0A23670h) ; , 0A23670h , ,
00A2365B push 3 ; ,1 ,3 ,0 ,
;
00A2365D mov ecx,dword ptr [ebp-0ECh] ; ecx,
00A23663 call X::`vector deleting destructor' (0A211EAh) ; vector deleting destructor
00A23668 mov dword ptr [ebp-10Ch],eax
00A2366E jmp main+0FAh (0A2367Ah)
00A23670 mov dword ptr [ebp-10Ch],0
13:
14: }
vector deleting destructor関数の符号化:
X::`vector deleting destructor':
00A21B10 push ebp
00A21B11 mov ebp,esp
00A21B13 sub esp,0CCh
00A21B19 push ebx
00A21B1A push esi
00A21B1B push edi
00A21B1C push ecx ; ,ecx ,
; , ecx
00A21B1D lea edi,[ebp-0CCh]
00A21B23 mov ecx,33h
00A21B28 mov eax,0CCCCCCCCh
00A21B2D rep stos dword ptr es:[edi]
;========================================== ====================
00A21B2F pop ecx ; ( ) , ecx,
00A21B30 mov dword ptr [ebp-8],ecx ; ebp-8
00A21B33 mov eax,dword ptr [ebp+8] ; eax
00A21B36 and eax,2 ; eax 2 , ( 0 1 3, 3 0)
00A21B39 je X::`vector deleting destructor'+61h (0A21B71h); 0,
; 0A21B71h
00A21B3B push offset X::~X (0A211DBh); ,
00A21B40 mov eax,dword ptr [this] ;this , eax
00A21B43 mov ecx,dword ptr [eax-4] ; 4byte (
; , ) ecx
00A21B46 push ecx ; ecx ,
00A21B47 push 4 ; ,
00A21B49 mov edx,dword ptr [this] ; edx
00A21B4C push edx ; edx ,
00A21B4D call `eh vector destructor iterator' (0A211F4h) ;
00A21B52 mov eax,dword ptr [ebp+8] ; ,
00A21B55 and eax,1; eax 1 , delete
00A21B58 je X::`vector deleting destructor'+59h (0A21B69h) ; 0, 0A21B69h ,
; ,
00A21B5A mov eax,dword ptr [this] ; eax
00A21B5D sub eax,4 ; eax 4, eax , ,eax
00A21B60 push eax ; eax , operator delete
00A21B61 call operator delete (0A21087h) ; operator delete,
00A21B66 add esp,4 ; operator delete ,
00A21B69 mov eax,dword ptr [this]; eax
00A21B6C sub eax,4 ; eax 4, eax , ,eax
00A21B6F jmp X::`vector deleting destructor'+80h (0A21B90h) ; 0A21B90h
;===================== 3 =====================
00A21B71 mov ecx,dword ptr [this] ; 3, 。 ecx,
00A21B74 call X::~X (0A211DBh) ;
00A21B79 mov eax,dword ptr [ebp+8] ; eax
00A21B7C and eax,1 ; ,
00A21B7F je X::`vector deleting destructor'+7Dh (0A21B8Dh) ; 0, 0A21B8Dh
; ,
00A21B81 mov eax,dword ptr [this] ; eax
00A21B84 push eax ; eax, operator delete
00A21B85 call operator delete (0A21087h) ; operator delete
00A21B8A add esp,4 ;operator delete ,
00A21B8D mov eax,dword ptr [this] ; eax,
00A21B90 pop edi
00A21B91 pop esi
00A21B92 pop ebx
00A21B93 add esp,0CCh
00A21B99 cmp ebp,esp
00A21B9B call @ILT+305(__RTC_CheckEsp) (0A21136h)
00A21BA0 mov esp,ebp
00A21BA2 pop ebp
00A21BA3 ret 4
vector deleting destructorの全体的な流れも、まずプロファイルエージェント関数を呼び出し、operator delete解放空間(オブジェクトフラグが0でない場合)を呼び出し、プロファイルエージェント関数を呼び出すときも4つのパラメータaを呼び出す)スタック空間の最初のオブジェクトヘッダアドレスb)対象サイズc)オブジェクト個数d)虚関数アドレス
次に、プロファイル・エージェント関数の集計符号化を示します.
mov ecx,dword ptr [ebp+0Ch] ; , ecx
00A236E0 imul ecx,dword ptr [ebp+10h] ;ebp+10h , ecx ebp+10h
; , ecx
;ecx
00A236E4 add ecx,dword ptr [ebp+8] ;ebp+8 , ecx
; ecx , ecx
;
00A236E7 mov dword ptr [ebp+8],ecx ; ecx ebp+8
00A236EA mov dword ptr [ebp-4],0
00A236F1 mov edx,dword ptr [ebp+10h] ; edx, for
00A236F4 sub edx,1 ;edx 1
00A236F7 mov dword ptr [ebp+10h],edx ; edx ebp+10h
00A236FA js `eh vector destructor iterator'+6Dh (0A2370Dh) ; edx-1 , 0A2370Dh 00A236FC mov eax,dword ptr [ebp+8] ; ebp+8 ( ) eax 00A236FF sub eax,dword ptr [ebp+0Ch] ;ebp+0ch , eax- ; , ; eax 00A23702 mov dword ptr [ebp+8],eax; eax ebp+8 00A23705 mov ecx,dword ptr [ebp+8]; ecx , 00A23708 call dword ptr [ebp+14h] ;ebp+14h , 00A2370B jmp `eh vector destructor iterator'+51h (0A236F1h); 0A236F1h ;
解析エージェント関数も、構築オブジェクトとは逆の順序でオブジェクトを解析するために、解析関数をループ呼び出していることがわかります.
単一のスタックオブジェクトに対してdeleteを呼び出す場合とスタックオブジェクト配列に対してdeleteを呼び出す場合とを比較すると、2つの場合の最も主要な違いは、最後にdeleteでスタック空間を解放する場合、delete[]がターゲットポインタ(すなわちdeleteにとって、ターゲットポインタはスタックオブジェクトヘッダアドレスであり、delete[]にとってスタック内の最初のスタックオブジェクトヘッダアドレスである)に対して4を減らす調整を行うことである.したがって,単一オブジェクトスタック空間を解放しdelete[]を誤って使用すると,中間検出を実行すると,オブジェクトタイプが3とマークされ,ターゲットポインタの調整が行われ,結果として誤ったスタック空間が解放される.同様に,オブジェクト配列を解放しdeletを誤って使用すると,中間検出を実行すると,オブジェクトタイプが1と判定され,ターゲットポインタ調整を行わずにスタック空間の解放にもエラーが発生する.
基本データ型
次はc++ソースです.
class X {
private:
int _x;
public:
X(int xx = 0) : _x(xx) {}
virtual ~X() {}
};
class Y {
private:
int _y;
public:
Y(int yy = 0) : _y(yy) {}
virtual ~Y() {}
};
class Z : public X, public Y {
private:
int _z;
public:
Z(int zz = 0) : _z(zz){}
virtual ~Z() {}
};
int main() {
int* ip1 = new int[2];
delete [] ip1;
int* ip2 = new int;
delete ip2;
}
c++コードのip 1は、スタック内の基本タイプint配列のヘッダアドレスを指し、ip 1は、スタック内の単一の基本タイプintのヘッダアドレスを指す.
次はmian関数の符号化です.
28: int* ip1 = new int[2];
00141A3E push 8 ; , operator new
00141A40 call operator new (141177h) ; operator new
00141A45 add esp,4 ; 4 operator new
00141A48 mov dword ptr [ebp-104h],eax ; eax ,
;
00141A4E mov eax,dword ptr [ebp-104h]
00141A54 mov dword ptr [ip1],eax ;eax ip1
29: delete [] ip1;
00141A57 mov eax,dword ptr [ip1] ; ip1 ( ) eax
00141A5A mov dword ptr [ebp-0F8h],eax ;
00141A60 mov ecx,dword ptr [ebp-0F8h]
00141A66 push ecx ;ecx , ecx , delete
00141A67 call operator delete (141082h) ; delete
00141A6C add esp,4 ; 4, delete
30:
31: int* ip2 = new int;
00141A6F push 4 ; , operator new
00141A71 call operator new (141177h); operator new
00141A76 add esp,4 ; 4 operator new
00141A79 mov dword ptr [ebp-0ECh],eax ; eax ,
;
00141A7F mov eax,dword ptr [ebp-0ECh]
00141A85 mov dword ptr [ip2],eax ;eax ip2
32: delete ip2;
00141A88 mov eax,dword ptr [ip2] ; ip2 ( ) eax
00141A8B mov dword ptr [ebp-0E0h],eax ;
00141A91 mov ecx,dword ptr [ebp-0E0h]
00141A97 push ecx ;ecx , ecx , delete
00141A98 call operator delete (141082h); delete
00141A9D add esp,4 ; 4, delete
符号化から,基本データ型には構造関数と構造関数がないため,この2つの場合は単純にnew割り当て空間を呼び出しdelete解放空間を呼び出すだけであることがわかる.また、スタック内のオブジェクト配列とは異なり、スタック内の基本タイプ配列が申請されたスタック空間ヘッダにオブジェクト個数を格納していないことも見られ、ip 1,ip 2はそれぞれ申請されたスタック空間ヘッダを直接指している.そのため、基本タイプではdeleteとdelete[]の効果は同じである.