free()は、異なるメモリブロックサイズのポインタをどのように解放しますか?
21302 ワード
最初はこの問題を知っていたC++delete[]配列サイズはどのように知っていますか?だったので、私も好奇心を持って仕事をしました.
メモリを申請するとき、ポインタがブロックのサイズを指しているという情報は、ポインタの周りに記録されています.次のコードを見てください.
(注:X 86のCPUの場合はsizeを8に変更してください)
wとnは常に一致していることがわかりますが、これは偶然ではありません.M$コンパイラvcincludeディレクトリの下malloc.を見てみましょう.hこのヘッダファイルの184行から209行のコード:
M$コンパイラでどのように解放されたかを見てみましょうmallloc.hファイル249行から274行:
SGI STL標準ライブラリソースstl_を見てみましょうalloc.hファイル209行から246行debug_allocクラステンプレートの設計:
gccの下を見てみると、実は似たようなデザインもあります.
実際、実際にコードを書く上で私たちを困惑させる問題の多くは、関連するソースコードを読むことで答えを得ることができます.
だから、オープンソースコードをよく読むのは、かなりメリットがあります.)
メモリを申請するとき、ポインタがブロックのサイズを指しているという情報は、ポインタの周りに記録されています.次のコードを見てください.
1 #include<cstdio>
2 #include<iostream>
3 #include<malloc.h>
4 #include<assert.h>
5 #include<ctime>
6 using namespace std;
7
8 #define size 16
9
10 int main(void)
11 {
12 void * p = NULL;
13 srand(time(0));
14 int a = 10;
15 while (a--)
16 {
17 int n = rand() % 10000;
18 p = malloc(n);
19 size_t w = *((size_t*)((char*)p - size));
20 cout << "w=" << w << endl;
21 cout << "n=" << n << endl;
22 assert(w == n);
23 free(p);
24 }
25 return 0;
26 }
(注:X 86のCPUの場合はsizeを8に変更してください)
wとnは常に一致していることがわかりますが、これは偶然ではありません.M$コンパイラvcincludeディレクトリの下malloc.を見てみましょう.hこのヘッダファイルの184行から209行のコード:
1 //
2 #if defined (_M_IX86)
3 #define _ALLOCA_S_MARKER_SIZE 8
4 #elif defined (_M_X64)
5 #define _ALLOCA_S_MARKER_SIZE 16
6 #elif defined (_M_ARM)
7 #define _ALLOCA_S_MARKER_SIZE 8
8 #elif !defined (RC_INVOKED)
9 #error Unsupported target platform.
10 #endif /* !defined (RC_INVOKED) */
11
12 _STATIC_ASSERT(sizeof(unsigned int) <= _ALLOCA_S_MARKER_SIZE);
13
14 #if !defined (__midl) && !defined (RC_INVOKED)
15 #pragma warning(push)
16 #pragma warning(disable:6540)
17 __inline void *_MarkAllocaS(_Out_opt_ __crt_typefix(unsigned int*) void *_Ptr, unsigned int _Marker)
18 {
19 if (_Ptr)
20 {
21 *((unsigned int*)_Ptr) = _Marker;
22 //
23 _Ptr = (char*)_Ptr + _ALLOCA_S_MARKER_SIZE;
24 // , _ALLOCA_S_MARKER_SIZE , , 。
25 }
26 return _Ptr;
27 }
M$コンパイラでどのように解放されたかを見てみましょうmallloc.hファイル249行から274行:
1 /* _freea must be in the header so that its allocator matches _malloca */
2 #if !defined (__midl) && !defined (RC_INVOKED)
3 #if !(defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC))
4 #undef _freea
5 __pragma(warning(push))
6 __pragma(warning(disable: 6014))
7 _CRTNOALIAS __inline void __CRTDECL _freea(_Pre_maybenull_ _Post_invalid_ void * _Memory)
8 {
9 unsigned int _Marker;
10 if (_Memory)
11 {
12 _Memory = (char*)_Memory - _ALLOCA_S_MARKER_SIZE;
13 //
14 _Marker = *(unsigned int *)_Memory;//
15 if (_Marker == _ALLOCA_S_HEAP_MARKER)
16 {
17 free(_Memory);
18 }
19 #if defined (_ASSERTE)
20 else if (_Marker != _ALLOCA_S_STACK_MARKER)
21 {
22 #pragma warning(suppress: 4548) /* expression before comma has no effect */
23 _ASSERTE(("Corrupted pointer passed to _freea", 0));
24 }
25 #endif /* defined (_ASSERTE) */
26 }
27 }
SGI STL標準ライブラリソースstl_を見てみましょうalloc.hファイル209行から246行debug_allocクラステンプレートの設計:
1 // Allocator adaptor to check size arguments for debugging.
2 // Reports errors using assert. Checking can be disabled with
3 // NDEBUG, but it's far better to just use the underlying allocator
4 // instead when no checking is desired.
5 // There is some evidence that this can confuse Purify.
6 template <class _Alloc>
7 class debug_alloc {
8
9 private:
10
11 enum {_S_extra = 8}; // Size of space used to store size. Note
12 // that this must be large enough to preserve
13 // alignment.
14
15 //
16 public:
17
18 static void* allocate(size_t __n)
19 {
20 //
21 8
22 char* __result = (char*)_Alloc::allocate(__n + (int) _S_extra);
23 *(size_t*)__result = __n;// 4 , , 4 , ,==。
24 return __result + (int) _S_extra;// 8
25 }
26
27 static void deallocate(void* __p, size_t __n)
28 {
29 char* __real_p = (char*)__p - (int) _S_extra;//
30 assert(*(size_t*)__real_p == __n);// ,
31 _Alloc::deallocate(__real_p, __n + (int) _S_extra);
32 }
33
34 static void* reallocate(void* __p, size_t __old_sz, size_t __new_sz)
35 {
36 char* __real_p = (char*)__p - (int) _S_extra;
37 assert(*(size_t*)__real_p == __old_sz);
38 char* __result = (char*)
39 _Alloc::reallocate(__real_p, __old_sz + (int) _S_extra,
40 __new_sz + (int) _S_extra);
41 *(size_t*)__result = __new_sz;
42 return __result + (int) _S_extra;
43 }
44
45 };
gccの下を見てみると、実は似たようなデザインもあります.
1 #if(defined(_X86_) && !defined(__x86_64))
2 #define _ALLOCA_S_MARKER_SIZE 8
3 #elif defined(__ia64__) || defined(__x86_64)
4 #define _ALLOCA_S_MARKER_SIZE 16
5 #endif
6
7 #if !defined(RC_INVOKED)
8 static __inline void *_MarkAllocaS(void *_Ptr,unsigned int _Marker) {
9 if(_Ptr) {
10 *((unsigned int*)_Ptr) = _Marker;
11 _Ptr = (char*)_Ptr + _ALLOCA_S_MARKER_SIZE;
12 }
13 return _Ptr;
14 }
15 #endif
1 #ifndef RC_INVOKED
2 #undef _freea
3 static __inline void __cdecl _freea(void *_Memory) {
4 unsigned int _Marker;
5 if(_Memory) {
6 _Memory = (char*)_Memory - _ALLOCA_S_MARKER_SIZE;
7 _Marker = *(unsigned int *)_Memory;
8 if(_Marker==_ALLOCA_S_HEAP_MARKER) {
9 free(_Memory);
10 }
11 #ifdef _ASSERTE
12 else if(_Marker!=_ALLOCA_S_STACK_MARKER) {
13 _ASSERTE(("Corrupted pointer passed to _freea",0));
14 }
15 #endif
16 }
17 }
18 #endif /* RC_INVOKED */
実際、実際にコードを書く上で私たちを困惑させる問題の多くは、関連するソースコードを読むことで答えを得ることができます.
だから、オープンソースコードをよく読むのは、かなりメリットがあります.)