首页 > 代码库 > free() 是如何释放不同内存区块大小的指针?
free() 是如何释放不同内存区块大小的指针?
最初是在知乎上看到这个问题的C++ delete[] 是如何知道数组大小的?,我也挺好奇,所以就作了一番工作。
申请内存时,指针所指向区块的大小这一信息,其实就记录在该指针的周围
看下面这段代码:
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 }
(注:如果是X86的CPU,请将 size 改为 8)
你会发现 w 和 n 始终是一致的,,这样其实不是巧合,来看 M$ 编译器 \ vc \include\ 目录下 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. Note12 // that this must be large enough to preserve13 // 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 _ASSERTE12 else if(_Marker!=_ALLOCA_S_STACK_MARKER) {13 _ASSERTE(("Corrupted pointer passed to _freea",0));14 }15 #endif16 }17 }18 #endif /* RC_INVOKED */
其实,很多在实际写代码中困惑我们的问题,都可以通过 阅读相关源代码来得到 答案。
所以,经常阅读那些开源代码,还是相当有好处的 :)
free() 是如何释放不同内存区块大小的指针?
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。