那就由我来给个详细的补充吧。
==========================
申请内存时,指针所指向区块的大小这一信息,其实就记录在该指针的
周围看下面这段代码:
#include<cstdio>#include<iostream>#include<malloc.h>#include<assert.h>#include<ctime>using namespace std;#define size 16int main(void){void * p = NULL;srand(time(0));int a = 10;while (a--){int n = rand() % 10000;p = malloc(n);size_t w = *((size_t*)((char*)p - size));cout << "w=" << w << endl;cout << "n=" << n << endl;assert(w == n);free(p);}return 0;}
(注:如果是X86的CPU,请将 size 改为 8)你会发现 w 和 n 始终是一致的,,这样其实不是巧合,来看 M$ 编译器 \ vc \include\ 目录下 malloc.h这一头文件 中 184 到 209 行的代码:
//这儿是根据不同的硬件平台的宏定义#if defined (_M_IX86)#define _ALLOCA_S_MARKER_SIZE 8#elif defined (_M_X64)#define _ALLOCA_S_MARKER_SIZE 16#elif defined (_M_ARM)#define _ALLOCA_S_MARKER_SIZE 8#elif !defined (RC_INVOKED)#error Unsupported target platform.#endif /* !defined (RC_INVOKED) */_STATIC_ASSERT(sizeof(unsigned int) <= _ALLOCA_S_MARKER_SIZE);#if !defined (__midl) && !defined (RC_INVOKED)#pragma warning(push)#pragma warning(disable:6540)__inline void *_MarkAllocaS(_Out_opt_ __crt_typefix(unsigned int*) void *_Ptr, unsigned int _Marker){ if (_Ptr) { *((unsigned int*)_Ptr) = _Marker; // _Ptr = (char*)_Ptr + _ALLOCA_S_MARKER_SIZE; //最后返回给调用者的指针,是原始指针偏移了_ALLOCA_S_MARKER_SIZE的新指针,这也是刚才我将指针向后偏移,就能得到该指针所指向内存区块的大小的原因。 } return _Ptr;}
再来看看在 M$ 编译器中它是如何释放的,同样在 mallloc.h 文件249行到274行:
/* _freea must be in the header so that its allocator matches _malloca */#if !defined (__midl) && !defined (RC_INVOKED)#if !(defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC))#undef _freea__pragma(warning(push))__pragma(warning(disable: 6014))_CRTNOALIAS __inline void __CRTDECL _freea(_Pre_maybenull_ _Post_invalid_ void * _Memory){ unsigned int _Marker; if (_Memory) { _Memory = (char*)_Memory - _ALLOCA_S_MARKER_SIZE;//获得原始指针 _Marker = *(unsigned int *)_Memory;//得到指针所指区块的大小 if (_Marker == _ALLOCA_S_HEAP_MARKER) { free(_Memory); }#if defined (_ASSERTE) else if (_Marker != _ALLOCA_S_STACK_MARKER) { #pragma warning(suppress: 4548) /* expression before comma has no effect */ _ASSERTE(("Corrupted pointer passed to _freea", 0)); }#endif /* defined (_ASSERTE) */ }}
再来看看 SGI STL标准库源码 stl_alloc.h 文件209 行到 246行 debug_alloc类模板的设计:
// Allocator adaptor to check size arguments for debugging.// Reports errors using assert. Checking can be disabled with// NDEBUG, but it's far better to just use the underlying allocator// instead when no checking is desired.// There is some evidence that this can confuse Purify.template <class _Alloc>class debug_alloc {private: enum {_S_extra = 8}; // Size of space used to store size. Note // that this must be large enough to preserve // alignment. //这儿就像它所说的那样public: static void* allocate(size_t __n) { //这里实际申请的内存大小要多 8 个字节 char* __result = (char*)_Alloc::allocate(__n + (int) _S_extra); *(size_t*)__result = __n;//前 4 个字节用于存储区块大小,可以看到,它预留了4个字节的空白区,具体原由 还望大牛能指出,==。 return __result + (int) _S_extra;//最后返回相对于原始指针偏移8个字节的新指针 } static void deallocate(void* __p, size_t __n) { char* __real_p = (char*)__p - (int) _S_extra;//获得原始指针 assert(*(size_t*)__real_p == __n);//这里增加了一个断言,防止析构了被破坏的指针 _Alloc::deallocate(__real_p, __n + (int) _S_extra); } static void* reallocate(void* __p, size_t __old_sz, size_t __new_sz) { char* __real_p = (char*)__p - (int) _S_extra; assert(*(size_t*)__real_p == __old_sz); char* __result = (char*) _Alloc::reallocate(__real_p, __old_sz + (int) _S_extra, __new_sz + (int) _S_extra); *(size_t*)__result = __new_sz; return __result + (int) _S_extra; }};
再来看看 gcc 下,其实也有类似的设计:
#if(defined(_X86_) && !defined(__x86_64))#define _ALLOCA_S_MARKER_SIZE 8#elif defined(__ia64__) || defined(__x86_64)#define _ALLOCA_S_MARKER_SIZE 16#endif#if !defined(RC_INVOKED) static __inline void *_MarkAllocaS(void *_Ptr,unsigned int _Marker) { if(_Ptr) { *((unsigned int*)_Ptr) = _Marker; _Ptr = (char*)_Ptr + _ALLOCA_S_MARKER_SIZE; } return _Ptr; }#endif
#ifndef RC_INVOKED#undef _freea static __inline void __cdecl _freea(void *_Memory) { unsigned int _Marker; if(_Memory) { _Memory = (char*)_Memory - _ALLOCA_S_MARKER_SIZE; _Marker = *(unsigned int *)_Memory; if(_Marker==_ALLOCA_S_HEAP_MARKER) {free(_Memory); }#ifdef _ASSERTE else if(_Marker!=_ALLOCA_S_STACK_MARKER) {_ASSERTE(("Corrupted pointer passed to _freea",0)); }#endif } }#endif /* RC_INVOKED */