程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> free() 是如何釋放不同內存區塊大小的指針?,free區塊

free() 是如何釋放不同內存區塊大小的指針?,free區塊

編輯:C++入門知識

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

其實,很多在實際寫代碼中困惑我們的問題,都可以通過 閱讀相關源代碼來得到 答案。

所以,經常閱讀那些開源代碼,還是相當有好處的 :)

 


C語言裡怎使用free()釋放字符串內存

由於free和malloc配對使用,malloc的時候,大小已經告訴系統了。

free的時候,系統會比對一下這個地址,是不是malloc申請的,如果不是,不給釋放

如果是的話,系統就知道要釋放多大內存,因為malloc的時候,系統有記錄的。

當然了,申請的地址,也記錄了的,否則,就無法比對了。

#include <stdio.h>
#include <stdlib.h>
int main()
{
int *p;

p=(int *)malloc(sizeof(int)*3);
p[0]=1;p[1]=2;p[2]=3;
p++;
//free(p);/////////由於p++過,已經不是malloc申請的那個地址了,因此這句運行會報錯
p[0]=4;
free(p-1);//這句倒是正確的,因為經過p++,這裡的p-1剛好是malloc申請的地址
return 0;
}

另外,問你一個同樣的問題:
char *p1 = new char[10];
char *p2 = new char[20];
delete p1[];
delete p2[];
釋放p1和p2的時候,沒有指明大小,它怎麼知道p1是10,p2是20的呢?
如果不知道,它怎麼去爭取的釋放它們呢?
 

C語言 :調用 free()釋放內存,系統報錯怎辦?還有個問題:free()怎知道要釋放多少內存?

1.你free的時候確定是malloc出來的指向空間?假設 p =malloc(...); 檢查一下是否在free之前對p重新進行了賦值...
2.free的時候,其實malloc出來的空間它的頭起部分是寫信息的,具體多少個字節我忘了。
我寫個偽代碼你看一下
void *malloc(size_t size)
{
char * p = 向系統申請空間 head_size +size;
在head_size內寫入申請的空間長度信息等。。
return p+head_size;
}
這樣free的時候,它根據頭信息,自然就知道釋放多少了。

具體建議查看一下內存的變化就知道了。
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved