柚子快報邀請碼778899分享:筆記 動態(tài)內存管理<C語言>
柚子快報邀請碼778899分享:筆記 動態(tài)內存管理<C語言>
導言
??????? 在C語言學習階段,指針、結構體和動態(tài)內存管理,是后期學習數據結構的最重要的三大知識模塊,也是C語言比較難的知識模塊,但是“天下無難事”,只要認真踏實的學習,也能解決,所以下文將介紹動態(tài)內存管理涉及到的一些函數以及概念。
目錄
導言
為什么存在動態(tài)內存管理
malloc和free
malloc
?free
calloc和realloc
calloc
?realloc
常見的關于動態(tài)內存管理錯誤
1.對可能是NULL指針的引用
?2.對不是動態(tài)開辟的內存進行釋放
3.對動態(tài)開辟的內存進行越界訪問
4.使用free釋放動態(tài)開辟內存的一部分
5.忘記內存釋放(忘記free),造成內存泄漏
例題
為什么存在動態(tài)內存管理
int a;
char arr[10];
這是我們常用的用于向內存申請空間的辦法,但是:
●空間開辟的空間是固定的
●數組在申明時,數組大小一旦確定,申請的內存空間不可變
在實際編寫程序時,可能我們對于內存空間的需求不是固定,那么使用動態(tài)內存管理自己申請空間、自己釋放空間就是一個很好的選擇。
malloc和free
malloc
函數參數及其返回值
void* malloc(size_t size);
//申請size個字節(jié)的空間
//返回值,成功申請:返回開辟空間的首地址、失?。悍祷豊ULL
注意點
●返回值是void*,那么我們在實際使用時,應把它強制轉化為我們需要的類型。
●與局部變量不同,開辟空間在堆區(qū)(如數組在棧區(qū))
●malloc不會將內存空間初始化為0,這是與最大calloc區(qū)別!
●動態(tài)內存可調整(通過realloc)
使用舉例:
?free
函數參數及其返回值
void free(void* ptr);
//釋放動態(tài)內存申請的ptr指向的空間
注意點
●只能用來手動釋放動態(tài)申請的空間,如果不是結果是未定義的
●釋放空間后,只是將權限交還于操作系統,指針還指向著地址(懸空指針),應該手動將其置為NULL
●如果釋放指針是NULL,那么什么也不做。
?使用舉例:
#include
int main() {
int* ptr = NULL;
int count = 0;
scanf("%d", &count);
ptr = (int*)malloc(count * sizeof(int));
free(ptr);
ptr = NULL;
return 0;
}
calloc和realloc
calloc
函數參數及其返回值
void* calloc(size_t num,size_t size);
//申請num個size個字節(jié)的空間,并初始化為0
//返回值,成功申請:返回開辟空間的首地址、失?。悍祷豊ULL
注意點
●開辟空間并全部初始化為0
●兩個參數(num個size字節(jié))
●其他與malloc類似
使用舉例:
?realloc
?函數參數及其返回值
void* realloc(void* ptr,size_t size);
//ptr是要調整的內存地址
//size是調整之后的大小
//返回值,成功申請:返回調整空間的首地址、失?。悍祷豊ULL
使用舉例:
#include
#include
int main() {
int count;
scanf("%d", &count);
int* ptr = (int*)calloc(count, sizeof(int));//申請count個int大小的空間
if (ptr) {//判斷是不是NULL:是否申請成功
for (int i = 0; i < count; i++)
ptr[i] = i;//賦值:從0開始到count-1步為1的序列
for (int i = 0; i < count; i++)
printf("%d ", ptr[i]);
}
printf("\n");
printf("調整前的地址:%p\n", ptr);//觀察動態(tài)(realloc)調整前的地址
int* p = (int*)realloc(ptr, (count + 5) * sizeof(int));
//申明一個新指針來接收,防止調整失敗返回NULL,數據丟失,調整為多5個int大小的地址
if (p)//判斷是否是NULL:是否調整成功
ptr = p;
printf("調整后的地址:%p", ptr);//觀察動態(tài)(realloc)調整后的地址
free(ptr);
ptr = NULL;
return 0;
}
運行結果:
先開辟10個int字節(jié)大小空間的運行結果:
?先開辟20個int字節(jié)大小空間的運行結果:
注意點
●參數size為0時,返回值是NULL,并將ptr的內存釋放,這是未定義的行為,在不同的編譯器上不能保證
●如果ptr參數為NULL,會動態(tài)開辟一個新的內存空間,此時realloc函數的作用等同于malloc
●這個函數調整空間時會把數據移到新的內存空間內(實際上,有一種情況不會,但是為了代碼的健壯性和可移植性,我們最好這樣定義)
兩種情況:
①原有地址后面有足夠的空間容納調整后的空間
②原有地址后面沒有足夠的空間容納調整后的空間
其實在前面的使用舉例中我們已經觀察到:
先開辟10個int字節(jié)大小空間的運行結果(第一種情況)
????????直接在原地址后面開辟新空間
先開辟20個int字節(jié)大小空間的運行結果(第二種情況)
??????? 找到一塊能容納調整后的空間的地址,將數據移動到其中
關于參數size為0時的舉例:
因為我們沒有辦法直接觀察一塊動態(tài)開辟的內存是否被釋放,且這種size為0行為是未定義的,所以我們只能觀察它的返回值
?
?關于參數ptr是NULL時的情況
此時realloc等同malloc
注意:動態(tài)內存管理的4個函數都包含在
常見的關于動態(tài)內存管理錯誤
1.對可能是NULL指針的引用
?
?2.對不是動態(tài)開辟的內存進行釋放
// 2.對不是動態(tài)開辟的內存進行釋放
#include
#include
int main() {
int a = 0;
int* p = &a;
free(p);
p = NULL;
return 0;
}
3.對動態(tài)開辟的內存進行越界訪問
//3.對動態(tài)開辟的內存進行越界訪問
#include
int main() {
int* p = (int*)malloc(sizeof(int));
p++;
*p = 1;
free(p);
p = NULL;
return 0;
}
4.使用free釋放動態(tài)開辟內存的一部分
//4.使用free釋放動態(tài)開辟內存的一部分
#include
int main() {
int* p = (int*)malloc(4*sizeof(int));//動態(tài)開辟4個int大小的空間
p++;//指向第二個元素
free(p);
p = NULL;
return 0;
}
5.忘記內存釋放(忘記free),造成內存泄漏
//5.忘記內存釋放(忘記free),造成內存泄漏
#include
int main() {
int* p = (int*)malloc(sizeof(int));
return 0;
}
例題
1.
void GetMemory(char* p)
{
p = (char*)malloc(100);
}
void Test(void)
{
char* str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
int main() {
Test();
return 0;
}運行會咋樣
p雖然在GetMemory函數中開辟了內存,但是在出函數時,該地址被銷毀,所以str還是NULL指針,對NULL指針進行賦值是一個未定義行為。(傳值調用而沒有使用傳址調用)
改正(二級指針):
void GetMemory(char** p)//使用二級指針接收
{
*p = (char*)malloc(100);
}
void Test(void)
{
char* str = NULL;
GetMemory(&str);//傳入指針的地址
strcpy(str, "hello world");
printf(str);
}
int main() {
Test();
return 0;
}
改正(將開辟的空間返回):
2.
char* GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
}
int main() {
Test();
return 0;
}//運行結果?
GetMemory函數返回了一個地址,但是這個地址出了函數,權限已經收回給了操作系統,str接收的是一個野指針,并將它打印出來,這種行為是未定義的,可能造成錯誤。(說到底是棧空間返回會被銷毀的問題)
我們知道只要是函數內的變量都是??臻g申請的空間,在出函數時,都會被回收,但是動態(tài)內存管理申請的空間,必須要手動釋放,所以在函數中我們使用動態(tài)內存申請的地址是不會被收回的(堆區(qū)申請),所以我們嘗試改正時,在函數內部使用動態(tài)內存申請,并返回。
改正:
char* GetMemory(void)
{
char* p = (char*)malloc(20);
strcpy(p, "hello world!");
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
}
int main() {
Test();
return 0;
}
3.
//3
void GetMemory(char** p, int num)
{
*p = (char*)malloc(num);
}
void Test(void)
{
char* str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
int main() {
Test();
return 0;
}//運行結果,以及問題
沒釋放空間,內存泄漏
改正:
void GetMemory(char** p, int num)
{
*p = (char*)malloc(num);
}
void Test(void)
{
char* str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
free(ptr);
ptr=NULL;
}
int main() {
Test();
return 0;
}
使用了已經被釋放的內存
柚子快報邀請碼778899分享:筆記 動態(tài)內存管理<C語言>
推薦文章
本文內容根據網絡資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉載請注明,如有侵權,聯系刪除。