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.  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 */

其实,很多在实际写代码中困惑我们的问题,都可以通过 阅读相关源代码来得到 答案。

所以,经常阅读那些开源代码,还是相当有好处的 :)

时间: 2024-08-05 07:08:42

free() 是如何释放不同内存区块大小的指针?的相关文章

手工释放linux内存

当在Linux下频繁存取文件后,物理内存会很快被用光,当程序结束后,内存不会被正常释放,而是一直作为caching.这个问题,貌似有不少人在问,不过都没有看到有什么很好解决的办法.那么我来谈谈这个问题.一.通常情况先来说说free命令:引用[[email protected] ~]# free -m         total used free shared buffers cachedMem: 249 163     86      0        10           94-/+ b

[转]手工释放linux内存——/proc/sys/vm/drop_caches

另一篇:http://www.linuxfly.org/post/320/ ? 1.清理前内存使用情况?free -m 2.开始清理??echo 1 > /proc/sys/vm/drop_caches 3.清理后内存使用情况?free -m 4.完成! 查看内存条数命令: dmidecode?|?grep?-A16?"Memory?Device$" ? ? ? ? ? +++++++++++++++++++++++++++++++++++++++++++++++++++++++

C语言中free函数是如何确定要释放多少内存空间的

本文链接:http://www.cnblogs.com/xxNote/p/4009359.html 今天看书的时候看到free函数释放动态申请的内存时只需要把内存块的首地址传过去就行了,显然仅仅依靠首地址是无法确定要释放多少内存的,猜想应该在某处存放着这个内存块的大小,网上搜了搜发现在Linux里面glibc在分配内存的时候会在内存块的地址前面的4个字节出存放内存块的大小,就猜想Windows里面应该也是这样.写了一个小程序测试了下: #include <stdio.h> #include &

手工释放linux内存——/proc/sys/vm/drop_caches

--手工释放linux内存--/proc/sys/vm/drop_caches 总有很多朋友对于Linux的内存管理有疑问,之前一篇日志似乎也没能清除大家的疑虑.而在新版核心中,似乎对这个问题提供了新的解决方法,特转出来给大家参考一下.最后,还附上我对这方法的意见,欢迎各位一同讨论.    当在Linux下频繁存取文件后,物理内存会很快被用光,当程序结束后,内存不会被正常释放,而是一直作为caching.这个问题,貌似有不少人在问,不过都没有看到有什么很好解决的办法.那么我来谈谈这个问题. 一.

【c/c++】内存分配大小

测试平台:linux 32位系统 用sizeof()运算符计算分配空间大小.单位:字节 1. 数组名与变量名的区别 int main() { char q[] = "hello"; cout << "q:" << sizeof(q) << endl; char *mq = q; cout << "mq:" << sizeof(mq) << endl; const char *

win10怎么关闭小娜助手释放电脑内存

不知道大家有没有觉得微软小娜助手在手机上挺实用,但是在电脑上任务栏上占用地方较大,真实的使用频率却很低,不仅仅如此而且小娜还会占用较多的电脑内存.今天我们就来说说怎么关闭小娜助手释放电脑内存. 一.同时按键盘上的Win+S键,打开小娜界面,点击左侧的齿轮图标. 二.把"Cortana可以提供建议.想法.提醒.通知等"这一项关闭,再将下面的两项也关掉. 三.点击[管理Cortana在云中了解到的我的相关内容],跳转至现金网官网并登陆账号,找到"其他Cortana数据以及个性化语

C++中类的内存空间大小(sizeof)分析

首先明确各数据类型占多大的空间.例如int到底是占2字节还是4字节空间: 在TC里,int是2字节的(主要是因为TC是16位的,所以int类型也该是16位的)VC++里,int是4字节的,因为现代操作系统下的软件大多是是32位.64位的VC++,本来按理说,该是8字节的,但是可能为了维持32位的源代码移植到64位尽量不出错,所以也维持了4字节的长度.至于其他有名的编译器,如gcc,我还没用过,你得查一查它所规定int的长度 或者利用sizeof(int)也可以计算出来.本人电脑上计算如下: 在C

虚函数列表: 取出方法 // 虚函数工作原理和(虚)继承类的内存占用大小计算 32位机器上 sizeof(void *) // 4byte

#include <iostream> using namespace std; class A { public: A(){} virtual void geta(){ cout << "A:A" <<endl; } virtual void getb(){ cout << "A:B" <<endl; } }; class B :public A{ public: B(){} virtual void g

C语言堆内存管理上出现的问题,内存泄露,野指针使用,非法释放指针

(1)开辟的内存没有释放,造成内存泄露 (2)野指针被使用或释放 (3)非法释放指针 (1)开辟的内存没有释放,造成内存泄露,下面的例子就可能造成20个字节的泄露,内存泄露不是一个立即会引发故障的错误,但是 它将消耗系统内存. void function1() { char *pa; pa = (char*)malloc(sizeof(char)*20); if(NULL !=pa) { strcpy(pa,"hello"); printf("pa = %x\n",