您好,登錄后才能下訂單哦!
這篇文章給大家介紹怎么在C語言中實現(xiàn)內(nèi)存管理,內(nèi)容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
C語言是一門面向過程的、抽象化的通用程序設計語言,廣泛應用于底層開發(fā),使用C語言可以以簡易的方式編譯、處理低級存儲器。
在計算機系統(tǒng),特別是嵌入式系統(tǒng)中,內(nèi)存資源是非常 有限的。尤其對于移動端開發(fā)者來說,硬件資源的限制使得其在程序設計中首要考慮的問題就是如何 有效地管理內(nèi)存資源。
常見內(nèi)存使用錯誤:
內(nèi)存申請未成功,就進行使用
內(nèi)存申請成功,但沒有初始化
內(nèi)存初始化成功,但越界訪問
忘記釋放內(nèi)存或者釋放一部分
內(nèi)存管理不當?shù)奈:Γ?/strong>
沒有初始化,會造成內(nèi)存出錯
越界訪問內(nèi)存可能導致崩潰
忘記釋放內(nèi)存造成內(nèi)存泄露
C語言的內(nèi)存管理:
C語言為用戶提供了相應內(nèi)存管理的AP接口,如 malloc()
,free()
,new()
等函數(shù),需要開發(fā)者手動管理。而java
、C#
則有自動內(nèi)存回收機制,基本無需再對內(nèi)存進行操作了。
由系統(tǒng)自動分配
堆區(qū)(heap)
在程序的執(zhí)行過程中才能分配,由程序員決定
全局區(qū)(靜態(tài)區(qū))
靜態(tài)區(qū)存放程序中所有的全局變量和靜態(tài)變量
常量區(qū)
常量字符串就是放在這里的
代碼段:
代碼段(code segment/text segment)。通常是指用來存放程序執(zhí)行代碼的一塊內(nèi)存區(qū)域。代碼區(qū)的指令中包括操作碼和要操作的對象(或?qū)ο蟮刂芬茫?。如果是立即?shù)(即具體的數(shù)值,如5
)直接包含在代碼中;如果是局部數(shù)據(jù),將在棧區(qū)分配空間,然后引用該數(shù)據(jù)地址。
數(shù)據(jù)段:
數(shù)據(jù)段(data segment)通常是指用來存放程序中已初始化的全局變量的一塊內(nèi)存區(qū)域。數(shù)據(jù)段屬于靜態(tài)內(nèi)存分配。
BSS段:
BSS段(Block Started by Symbol)。指用來存放程序中未初始化的全局變量的一塊內(nèi)存區(qū)域。
BSS段本質(zhì)上也屬于數(shù)據(jù)段,都用來存放C程序中的全局變量。區(qū)別在于.data段中存放初始化為非零的全局變量,而把顯式初始化為0或者并未顯式初始化(C語言規(guī)定未顯式初始化的全局變量值默認為0)
的全局變量存在BSS段。
由編譯器 自動分配釋放,存放函數(shù)的參數(shù)值、局部變量的值等,是一種先進后出的內(nèi)存結(jié)構(gòu)。
哪些是分配在??臻g?
局部變量的值存放在棧上
在函數(shù)體中定義的變量通常是在棧上
函數(shù)棧分配:
在函數(shù)調(diào)用時,第一個進棧的是主函數(shù)中函數(shù)調(diào)用后的下一條指令(函數(shù)調(diào)用語句的下一條可執(zhí)行語句)的地址,然后是函數(shù)的各個參數(shù),在大多數(shù)的C編譯器中,參數(shù)是由右往左入棧的,然后是函數(shù)中的局部變量。
棧內(nèi)存什么時候回收?
棧內(nèi)存的分配和釋放也由編譯器在函數(shù)進入和退出時插入指令自動完成,生命周期和函數(shù)、局部變量一樣。
棧空間的大?。?/strong>
在 Windows下,棧是向低地址擴展的數(shù)據(jù)結(jié)構(gòu),是塊連續(xù)的內(nèi)存的區(qū)域。??臻g一般較小,棧大小與編譯器有關。默認情況下,visual studio 2010
的棧大小為1M
。但在平時應用程序中,由于函數(shù)會使用棧結(jié)果,所以只能用略小于1M大小的棧如果申請的空間超過棧的剩余空間時,將提示Stack overflow。
示例代碼:
#include<stdio.h> struct A {}; class B {}; void fun(int a , int b) //參數(shù)a,b在棧上, 在函數(shù)體結(jié)束的時候,棧內(nèi)存釋放 { int c;//局部變量,在棧上, 在函數(shù)體結(jié)束的時候,棧內(nèi)存釋放 } int main() { int a = 10;//局部變量在棧上, 在main函數(shù)結(jié)束的時候,棧內(nèi)存釋放 char b[] = "hello";//數(shù)組變量也在棧上, 在main函數(shù)結(jié)束的時候,棧內(nèi)存釋放 char *c = NULL;//在棧上, 在main函數(shù)結(jié)束的時候,棧內(nèi)存釋放 { A d;//結(jié)構(gòu)體變量, 在棧上 B e;//類對象在棧上 } //d,e 在離開這個{}時,棧內(nèi)存銷毀釋放 //測試棧的大小 //char buf[1024 * 1024] = { 'A' };//1M時崩潰了 char buf[1000* 1024] = { 'A' };//??臻g略小于1M //經(jīng)過編譯期設置為5M之后,??臻g變大了 char buf[49 * 1024 * 1024 / 10] = { 'A' };//棧空間略小于5M printf("%d" , sizeof(buf) ); return 0; }
需程序員自己申請,并可在運行時指定空間大小,并由程序員手動進行釋放,容易產(chǎn)生 memory leak
。
哪些是分配在堆空間?
調(diào)用 malloc
,realloc
,calloc
函數(shù)
//分配得來得10*4字節(jié)的區(qū)域在堆區(qū) p1 = (char*)malloc(10*sizeof(int));
堆空間需要手動釋放:
堆是由 malloc()
等函數(shù)分配的內(nèi)存塊,內(nèi)存釋放由程序員調(diào)用free()
函數(shù)手動釋放
堆空間的大小:
堆空間一般較大,與64位/32位,編譯器有關,受限于計算機系統(tǒng)中有效的虛擬內(nèi)存;理論上32位系統(tǒng)堆內(nèi)存可以達到4G的空間,實際上2G以內(nèi),64位128G以內(nèi)(虛擬內(nèi)存16TB)
示例代碼:
#include<stdio.h> #include<stdlib.h> int main() { //手動分配、這里就是分配了堆內(nèi)存 int *p = (int*)malloc(10 * sizeof(int )); //手動釋放 free(p); int MB = 0; while (malloc(1024 * 1024))//每次分配1M { MB++; } printf("分配了 %d MB \n", MB); return 0; }
棧與堆的區(qū)別:
類型 | 分配釋放 | 大小 | 是否連續(xù) | 申請效率 |
---|---|---|---|---|
棧區(qū) | 由編譯器自動分配釋放 | 較小 | 一塊連續(xù)的內(nèi)存區(qū)域 | 由系統(tǒng)自動分配,速度快 |
堆區(qū) | 由程序員分配釋放 | 較大 | 堆是向高地址擴展的數(shù)據(jù)結(jié)構(gòu),是不連續(xù)的內(nèi)存區(qū)域 | 速度慢,容易產(chǎn)生內(nèi)存碎片 |
全局變量和靜態(tài)變量的存儲是放在一塊的,初始化的全局變量和靜態(tài)變量在塊區(qū)域。
哪些是分配在全局靜態(tài)區(qū)?
全局變量
static靜態(tài)變量
全局靜態(tài)區(qū)何時釋放?
全局變量、靜態(tài)變量在整個程序運行的生存期都存在,所以在程序結(jié)束時才釋放
示例代碼:
#include<stdio.h> //儲存在全局靜態(tài)區(qū) int a; //全局變量,未初始化 short b = 10; //全局變量,已賦值 char *c = NULL; //全局變量,已賦值 static int f = 200; //靜態(tài)變量 int main() { static int d = 100; static int e = 200; printf("%p\n", &a); printf("%p\n", &b); printf("%p\n", &c); printf("%p\n", &d); printf("%p\n", &e); printf("%p\n", &f); }
字符串常量是放在常量區(qū),當你初始化賦值的時候,這些常量就先在常量區(qū)開辟一段空間,保存此常量,以后相同的常量就都使用一個地址。
示例代碼:
#include<stdio.h> //“AAA”是字符串常量,存放在常量區(qū) char *p = "AAA"; int main() { //p1是局部變量,在棧上, “AAA”是字符串常量,存放在常量區(qū) char *p1 = "AAA"; //p2是局部變量,在棧上,“AAA”不是字符串常量,她只是一種初始化的寫法 char p2[]= "AAA"; //p3是局部變量,在棧上, “AAA”是字符串常量,存放在常量區(qū) char *p3 = "AAA"; //p4是局部變量,在棧上, “AAB”是字符串常量,存放在常量區(qū) char *p4 = "AAB"; printf("%p\n", p); printf("%p\n", p1); printf("%p\n", p2); printf("%p\n", p3); printf("%p\n", p4); }
三個函數(shù)的作用?
它們都能分配堆內(nèi)存、成功返回內(nèi)存的首地址,失敗就返回NULL
。
malloc
函數(shù):
void *malloc( size_t size );
該函數(shù)將在堆上分配一個 size
byte大小的內(nèi)存。不對內(nèi)存進行初始化,所以新內(nèi)存其值將是隨機的。
calloc
函數(shù):
void *calloc( size_t number, size_t size );
該函數(shù)功能與 malloc
相同,它將分配count
個size
大小的內(nèi)存,自動初始化該內(nèi)存空間為零。
realloc
函數(shù):
void *realloc( void *memblock, size_t size );
該函數(shù)將ptr
內(nèi)存大小增大或減小到newsize
。
realloc
函數(shù)返回的兩種情況:
如果當前連續(xù)內(nèi)存塊足夠 realloc
的話,只是將p1所指向的空間擴大,并返回p1的指針地址。
如果當前連續(xù)內(nèi)存塊不夠長度,再找一個足夠長的地方,分配一塊新的內(nèi)存p2,并將p1指向的內(nèi)容Copy到p2,并釋放p1指向的舊內(nèi)存,然后返回p2。
示例代碼:
#include<stdio.h> #include<stdlib.h> int main() { //malloc ,參數(shù)是字節(jié)數(shù) , 并且這塊內(nèi)存空間的值是隨機的 int *p = (int *)malloc(5 * sizeof(int)); p[0] = 123; for (int i = 0; i < 5; ++i) { printf("%d ", p[i]); //后面4個值隨機 } printf("\n------------------------------------------------------------\n " ); //calloc,參數(shù)兩個, 自動將內(nèi)存空間初始化為0 int *p2 = (int *)calloc(5, sizeof(int)); p2[4] = 123; for (int i = 0; i < 5; ++i) { printf("%d ", p2[i]); } printf("\n------------------------------------------------------------\n "); //realloc ,可以調(diào)整內(nèi)存空間的大小 ,并且拷貝原來的內(nèi)容(調(diào)大,或者 縮小) //int *p3 =(int *) realloc(p, 6* sizeof(int));//調(diào)大一點點,兩個地址相同 //int *p3 = (int *)realloc(p, 2 * sizeof(int));//縮小,兩個地址相同 int *p3 = (int *)realloc(p, 100 * sizeof(int));//調(diào)很大,兩個地址不同 ,釋放原來的內(nèi)存空間 for (int i = 0; i <2; ++i) { printf("%d ", p3[i]); } printf("\np地址: %p , p3的地址: %p ", p, p3); return 0; }
頭文件:
#include <string.h>
strcpy
函數(shù)
char *strcpy( char *strDestination, const char *strSource );
把src
所指由\0
結(jié)束的字符串復制到dest
所指的數(shù)組中。
注意事項:
src
和dest
所指內(nèi)存區(qū)域不能重疊,且dest
必須有足夠的空間來容納src
的字符串,src
的結(jié)尾必須是'\0'
,返回指向dest
的指針。
memcpy
函數(shù)
void *memcpy( void *dest, const void *src, size_t count );
由src
所指內(nèi)存區(qū)域復制 count
個字節(jié)到dest
所指內(nèi)存區(qū)域。
注意事項:
函數(shù)返回指向dest
的指針和 strcpy
相比,memcpy
不是遇到\0
就結(jié)束,而一定會拷貝n
個字節(jié)注意src
和dest
所指內(nèi)存區(qū)域不能重疊,否則不能保證正確。
memmove
函數(shù)
void *memmove( void *dest, const void *src, size_t count );
函數(shù)功能:與 memcpy
相同。
注意事項:
src
和dest
所指內(nèi)存區(qū)域可以重疊,memmove
可保證拷貝結(jié)果正確,而memcpy
不能保證。函數(shù)返回指向dest
的指針。
memset
函數(shù)
void *memset( void *dest, int c, size_t count );
常用于內(nèi)存空間的初始化。將已開辟內(nèi)存空間s
的首n
個字節(jié)的值設為值c
,并返回s
。
示例代碼:
#include<stdio.h> #include<string.h> #include<assert.h> //模擬memcpy函數(shù)實現(xiàn) void * MyMemcpy(void *dest, const void *source, size_t count) { assert((NULL != dest) && (NULL != source)); char *tmp_dest = (char *)dest; char *tmp_source = (char *)source; while (count--)//不判斷是否重疊區(qū)域拷貝 *tmp_dest++ = *tmp_source++; return dest; } //模擬memmove函數(shù)實現(xiàn) void * MyMemmove(void *dest, const void *src, size_t n) { char temp[256]; int i; char *d =(char*) dest; const char *s =(char *) src; for (i = 0; i < n; i++) temp[i] = s[i]; for (i = 0; i < n; i++) d[i] = temp[i]; return dest; } int main() { //strcpy進行字符串拷貝 //注意: 1. src字符串必須以'\0'結(jié)束, 2. dest內(nèi)存大小必須>=src char a[5]; //char b[5] = "ABC";//字符串結(jié)尾會自動的有\(zhòng)0 , 此處 b[4]就是'\0' char b[5]; b[0] = 'A'; b[1] = 'B'; b[2] = 'C'; b[3] = '\0';//必須加\0,否則strcpy一直向后尋找\0 strcpy(a, b); printf("%s\n", a); //memcpy函數(shù), 直接拷貝內(nèi)存空間,指定拷貝的大小 int a2[5]; int b2[5] = { 1,2,3,4,5 };//不需要'\0'結(jié)束 memcpy(a2, b2, 3 *sizeof(int) );//指定拷貝的大小, 單位 字節(jié)數(shù) printf("%d , %d ,%d\n" , a2[0] , a2[1], a2[2]); MyMemcpy(a2 + 3, b2 + 3, 2 * sizeof(int)); printf("%d , %d \n", a2[3], a2[4]); //演示內(nèi)存重疊的情況 char a3[6] = "123"; //MyMemcpy(a3 + 1, a3, 4); //得到11111 memcpy(a3 + 1, a3, 4);//雖然它是正確的,但是不保證,重疊拷貝應該避免使用它 printf("%s\n", a3); //memmove功能與memcpy一樣,但是了考慮了重疊拷貝的問題,可以保證正確 char a4[6] = "123"; //MyMemmove(a4 + 1, a4, 4);//可以保證正確 memmove(a4 + 1, a4, 4);//可以保證正確 printf("%s\n", a4); //memset比較簡單, 把內(nèi)存區(qū)域初始化化為某個值 char a5[6]; memset(a5, 0, 6); for (int i = 0; i < 6; ++i) { printf("%d", a5[i]); } return 0; }
思路:
利用 realloc
函數(shù),當數(shù)組元素滿的時候,擴充內(nèi)存區(qū)域,然后加入元素!
示例代碼:
#include<stdio.h> #include<stdlib.h> #include<assert.h> //為了代碼的可讀性,將設計為C++中的類,利用struct 代替 //動態(tài)數(shù)組 struct Array { //自動構(gòu)造函數(shù),它初始化 Array() { grow = 3; size = 3; n = 0; //分配并初始化內(nèi)存 pHead = (int *)calloc(size , sizeof(int)); assert(pHead != NULL); } void AddElem(int e) { if (n >= size)//說明數(shù)組滿了 { //需要擴大內(nèi)存 size += grow; pHead = (int *)realloc( pHead, size * sizeof(int) ); assert(pHead != NULL); } pHead[n++] = e; //添加元素 } void Print() { printf("\n\n數(shù)組總空間:%d , 元素個數(shù): %d \n", size, n); for (int i = 0; i < n; ++i) { printf("%d " , pHead[i]); } } int size;//總空間, 不是固定的,可以增大的 int n;//當前數(shù)組的元素個數(shù) int grow;//每次數(shù)組內(nèi)存滿了的時候,增長量 int *pHead;//數(shù)組的起始地址 }; int main() { Array arr; arr.AddElem(1); arr.AddElem(2); arr.AddElem(3); arr.AddElem(4); arr.AddElem(5); arr.AddElem(6); arr.AddElem(7); arr.AddElem(8); arr.Print(); arr.AddElem(11); arr.AddElem(22); arr.AddElem(33); arr.AddElem(44); arr.AddElem(55); arr.Print(); return 0; }
何謂內(nèi)存訪問越界,簡單的說,你向系統(tǒng)申請了一塊內(nèi)存,在使用這塊內(nèi)存的時候,超出了你申請的范圍。
訪問到野指針指向的區(qū)域,越界訪問
數(shù)組下標越界訪問
使用已經(jīng)釋放的內(nèi)存
企圖訪問一段釋放的??臻g
容易忽略 字符串后面的'\0'
注意:
strlen
所作的是一個計數(shù)器的工作,它從內(nèi)存的某個位置(可以是字符串開頭,中間某個位置,甚至是某個不確定的內(nèi)存區(qū)域)開始掃描,直到碰到第一個字符串結(jié)束符'\0'
為止,然后返回計數(shù)器值( 長度不包含'\0')。
示例代碼:
#include<stdio.h> #include<stdlib.h> #include<string.h> char * fun() { char arr[10]; return arr; }//arr是棧內(nèi)存,離開此花括號,棧被釋放回收 int main() { //1.訪問到野指針指向的區(qū)域,越界訪問 char *p;//沒有初始化,野指針,亂指一氣 //strcpy(p, "hello");//非法越界訪問 //2.數(shù)組下標越界訪問 int * p2 = (int *)calloc(10, sizeof(int)); for (size_t i = 0; i <= 10; i++) { p2[i] = i;//很難察覺的越界訪問, 下標越界 } //3.使用已經(jīng)釋放的內(nèi)存 char *p3 = (char *)malloc(10); free(p3); if (p3 != NULL)//這里if不起作用 { strcpy(p3, "hello");//錯誤,p3已經(jīng)被釋放 } //4.企圖訪問一段釋放的??臻g char *p4 = fun(); //p4指向的棧空間已經(jīng)被釋放 strcpy(p4, "hello"); printf("%s\n",p4); //5.容易忽略 字符串后面的'\0' char *p5 = (char *)malloc(strlen("hello"));//忘記加1 strcpy(p5, "hello");//導致p5的長度不夠,越界 return 0; }
11. 內(nèi)存泄露(Memory Leak)
是指程序中己動態(tài)分配的堆內(nèi)存由于某種原因程序未釋放或無法釋放,造成系統(tǒng)內(nèi)存的浪費,導致程序運行速度減慢甚至系統(tǒng)崩潰等嚴重后果。
丟失了分配的內(nèi)存的首地址,導致無法釋放
丟失分配的內(nèi)存地址
企圖希望傳入指針變量獲取對內(nèi)存,殊不知是拷貝
每循環(huán)一次,泄露一次內(nèi)存
非法訪問常量區(qū)
示例代碼:
#include<stdio.h> #include<stdlib.h> #include<string.h> char * GetBuf() { return (char *)malloc(10); } void GetBuf2(char *p)//p已經(jīng)是一份拷貝,和原參數(shù)無任何關系 { p= (char *)malloc(10); } char * GetBuf3() { char *p = "hello";//常量內(nèi)存區(qū),不可更改 return p; } int main() { //1.丟失了分配的內(nèi)存的首地址,導致無法釋放 GetBuf();//忘記接收返回值了 //2.丟失分配的內(nèi)存地址 char *p1= (char *)malloc(10); char *p2 = (char *)malloc(10); p1 = p2;//這一步,導致第一次分配的堆內(nèi)存丟失,無法釋放 //3.企圖希望傳入指針變量獲取對內(nèi)存,殊不知是拷貝 char *p3 = NULL; GetBuf2(p3); //應該使用指針的指針,或者引用 //strcpy(p3, "hello"); //錯誤,這里的p3仍然為NULL //4.每循環(huán)一次,泄露一次內(nèi)存 char * p4 = NULL; for (int i = 0; i < 10; ++i) { p4= (char *)malloc(10); } strcpy(p4, "hello"); // 這里的p4只指向最后一次分配的,前面的全部內(nèi)存泄漏 //5.非法訪問常量區(qū) char *p5 = GetBuf3(); strcpy(p5, "hello"); return 0; }
內(nèi)存碎片:
內(nèi)存碎片一般是由于空閑的內(nèi)存空間比要連續(xù)申請的空間小,導致這些小內(nèi)存塊不能被充分的利用,當你需要分配大的連續(xù)內(nèi)存時,盡管剩余內(nèi)存的總和足夠,但系統(tǒng)找不到連續(xù)的內(nèi)存,所以導致分配失敗malloc/free
大量使用會造成內(nèi)存碎片
為什么會產(chǎn)生內(nèi)存碎片?
如果有100個單位的連續(xù)空閑內(nèi)存,那么先申請5單元的連續(xù)內(nèi)存,再申請50單元的內(nèi)存這時釋放一開始的5單元的內(nèi)存。這時,如果你一直申請比5單元大的內(nèi)存單元,那么開始的那連續(xù)的5單元就一直不能被使用。
內(nèi)存池技術(shù):
內(nèi)存的申請、釋放是低效的,我們只在開始申請一塊大內(nèi)存(不夠繼續(xù)申請),然后每次需要時都從這塊內(nèi)存取出,并標記這塊內(nèi)存是否被使用。釋放時僅僅標記而不真的free
,只有內(nèi)存都空閑的時候,才釋放給操作系統(tǒng)。這樣減少了 malloc
、free
次數(shù),從而提高效率。
設計思路:
先分配幾個大的連續(xù)內(nèi)存塊(MemoryBlock),每個內(nèi)存塊用鏈表鏈接起來,然后通過一個內(nèi)存池結(jié)構(gòu)(MemoryPool)管理!
代碼實現(xiàn):
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<assert.h> class MemoryBlock { public: int nSize; //該內(nèi)存塊的總大小 (單元個數(shù)X每個單元大小),以字節(jié)為單位 int nFree; //該內(nèi)存塊還有多少個空閑的單元 int nFirst; //當前可用空閑單元的序號,從0開始 MemoryBlock* pNext; //指向下一個MemoryBlock內(nèi)存塊 char aData[1]; //用于標記分配內(nèi)存開始的位置 //.....這個結(jié)構(gòu)下面全是內(nèi)存 public: MemoryBlock(int unitCount, int unitSize) { nSize = unitCount* unitSize; nFree = unitCount; nFirst = 0; pNext = NULL; char *p = aData;//獲取內(nèi)存單元的首地址 for (int i = 0; i < unitCount -1; ++i) { *((short *)p) = i + 1; //第0塊的下個空閑索引是不是第1塊 p += unitSize; } *((short *)p) = -1;//最后一塊沒有下一個空閑空間了,為-1 } void * operator new (size_t t, int size) { int headSize = sizeof(MemoryBlock); return ::operator new(headSize + size); } }; //分配固定內(nèi)存的內(nèi)存池 class MemoryPool { public: //初始大小 (每一個MemoryBlock中初始的單元個數(shù)) int nInitCount; //(后面增加的MemoryBlock中單元個數(shù)) int nGrowSize; //分配單元大小,MemoryBlock中每個單元的大小 int nUnitSize; //內(nèi)存塊鏈表 MemoryBlock* pBlock; public: MemoryPool( int _nInitCount, int _nGrowSize, int _nUnitSize) { nInitCount = _nInitCount; nGrowSize = _nGrowSize; nUnitSize = _nUnitSize; pBlock = NULL; } char * Alloc() //每次只返回 nUnitSize 大小的內(nèi)存 { if (pBlock == NULL) { MemoryBlock * p =(MemoryBlock *) new (nInitCount * nUnitSize) MemoryBlock(nInitCount, nUnitSize); assert(p != NULL); pBlock = p; } MemoryBlock * pB = pBlock; while (pB !=NULL && pB->nFree==0) { pB = pB->pNext; } if (pB == NULL)//一直沒找到了可以分配的MemoryBlock,說明內(nèi)存池已滿 { pB = (MemoryBlock *) new (nGrowSize * nUnitSize) MemoryBlock(nGrowSize, nUnitSize); assert(pB != NULL); pB->pNext = pBlock; pBlock = pB; } //得到第一個可用的空閑內(nèi)存地址 char *pFree = pB->aData + pB->nFirst * nUnitSize; //把nFirst值改為下一個空閑的索引 (存儲在當前內(nèi)存的前兩個字節(jié)) pB->nFirst = *((short*)pFree); pB->nFree--; return pFree; } void Free(void *p) { //考慮這個地址落在哪個 MemoryBlock 上 MemoryBlock * pB = pBlock; while (pB != NULL && p < pB->aData || p > pB->aData+ pB->nSize ) { pB = pB->pNext; } if (pB!= NULL)//找到了p所在的MemoryBlock { //銷毀之前先讓它的前兩個字節(jié)指向nFirst (當前空閑的索引) *((short*)p) = pB->nFirst; //nFirst的值指向當前釋放的 pB->nFirst = ((char *)p - pB->aData) / nUnitSize; pB->nFree++; } else { printf("錯誤,此內(nèi)存并非內(nèi)存池分配的!\n"); } } void Print() { printf("\n\n\n"); MemoryBlock * pB = pBlock; while (pB != NULL ) { printf("\n首地址:%p 總大?。?d 空閑個數(shù): %d 下一個空閑:%d \n", pB->aData , pB->nSize, pB->nFree ,pB->nFirst); for (int i = 0; i < pB->nSize / nUnitSize; ++i) { printf("\t %d" , * ((int *) ( pB->aData + i * nUnitSize ))); } pB = pB->pNext; printf("\n---------------------------------------------------------\n"); } } }; int main() { MemoryPool pool(3, 3, 4); int *p1 = (int *)pool.Alloc(); *p1 = 111; int *p2 = (int *)pool.Alloc(); *p2 = 222; int *p3 = (int *)pool.Alloc(); *p3 = 333; pool.Print(); int *p4 = (int *)pool.Alloc(); *p4 = 444; pool.Print(); int *p5 = (int *)pool.Alloc(); *p5 = 555; pool.Print(); pool.Free( p1); pool.Free(p2); pool.Free(p3); pool.Print(); p1 = (int *)pool.Alloc(); *p1 = 111; p2 = (int *)pool.Alloc(); *p2 = 222; p3 = (int *)pool.Alloc(); *p3 = 333; pool.Print(); return 0; }
關于怎么在C語言中實現(xiàn)內(nèi)存管理就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。