C語言中動(dòng)態(tài)內(nèi)存分配malloc、calloc和realloc函數(shù)解析
前言
有時(shí)候我們需要的空間大小不確定,需要隨著程序需要的空間而變化, 那以數(shù)組開辟的固定大小的空間就不適用了, 這時(shí)候我們就需要?jiǎng)討B(tài)分配開辟空間了。當(dāng)空間不夠時(shí)就擴(kuò)容。動(dòng)態(tài)開辟是在堆區(qū)開辟一塊連續(xù)可用空間,并返回這塊空間的地址。有三種函數(shù)malloc, calloc和realloc。我們動(dòng)態(tài)內(nèi)存分配就在堆區(qū)開辟空間
上面的四個(gè)區(qū)只有堆區(qū)的空間是需要手動(dòng)釋放的
free函數(shù)
free函數(shù)是專門用來對(duì)動(dòng)態(tài)開辟內(nèi)存的回收和釋放的。當(dāng)我們不需要再使用動(dòng)態(tài)開辟的空間時(shí),一定要free釋放空間,因?yàn)槭窃诙焉祥_辟的空間,所以不會(huì)隨著出了作用域而銷毀,需要我們free釋放,避免內(nèi)存泄漏,并置空(將指針置為NULL),避免形成野指針。
內(nèi)存釋放是標(biāo)記刪除, 只會(huì)修改當(dāng)前空間的所屬狀態(tài),并不會(huì)清除空間內(nèi)容。
當(dāng)然內(nèi)存泄漏也不是都有危害,但為了養(yǎng)成良好的代碼習(xí)慣,動(dòng)態(tài)開辟后一定要free
void free (void* ptr);
1.如果參數(shù)ptr指向的內(nèi)存空間不是動(dòng)態(tài)內(nèi)存開辟的,那free函數(shù)的行為是未定義的,就會(huì)報(bào)錯(cuò),所以free函數(shù)是針對(duì)動(dòng)態(tài)開辟的空間
2.如果ptr是 NULL空指針,那么free函數(shù)什么都不做。
下面我們來實(shí)現(xiàn)一下。
動(dòng)態(tài)內(nèi)存分配需要調(diào)用頭文件#include<stdlib.h>
malloc函數(shù)
void* malloc (size_t size);
malloc函數(shù)用來動(dòng)態(tài)開辟的, size是開辟所需空間的大小,單位:字節(jié)。
并返回起始地址。返回類型是void*,表示我們可以開辟任意類型的空間,同時(shí)我們需要強(qiáng)制類型轉(zhuǎn)換成我們開辟空間類型的指針,再用指針接收
例如我們開辟的是int型的空間,就需要先轉(zhuǎn)換為(int*),再用int*的指針接收
開辟float型的空間,就需要先轉(zhuǎn)換為(float*),用于float*的指針接收
int* p = (int*)malloc(sizeof(int) * 10);
重點(diǎn):
1.malloc函數(shù)在開辟空間后需要判斷開辟空間是否成功,若開辟成功會(huì)返回開辟好的空間的指針,開辟失敗會(huì)返回空指針。
2.malloc函數(shù)并不會(huì)對(duì)開辟空間進(jìn)行初始化,空間內(nèi)容為隨機(jī)數(shù)。
下面是關(guān)于malloc函數(shù)的例子,主函數(shù)如下:
主函數(shù)中第一段代碼就是在堆上動(dòng)態(tài)開辟10個(gè)int型大小的空間,通過int*型的指針p去維護(hù)這塊空間,如果空間開辟失敗就會(huì)返回NULL,所以我們需要判斷是否開辟成功。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> int main() { int* p = (int*)malloc(sizeof(int) * 10); if (p == NULL) { //我們也可以直接eixt(-1)異常退出,或是return; printf("%s\n", strerror(errno)); //strerror(errno)是用來判斷開辟失敗的錯(cuò)誤原因 return -1; //error是錯(cuò)誤碼 } //需要調(diào)用最下面的兩個(gè)頭文件 for (int i = 0; i < 10; i++) { printf("%d ", p[i]); } printf("\n"); return 0; }
判斷p為NULL時(shí),也可以直接打印開辟失敗,不需要調(diào)用strerror函數(shù),也不用包含那兩個(gè)頭文件
if (p == NULL) { printf("malloc failed\n"); exit(-1); }
calloc函數(shù)
void* calloc (size_t num, size_t size);
calloc函數(shù)與malloc類似 ,calloc可以看作malloc+memset
參數(shù)num是開辟空間的元素個(gè)數(shù),參數(shù)size是元素的大小,單位:字節(jié)。
函數(shù)的功能是為 num 個(gè) size大小 的元素開辟一塊空間,并且把空間的每個(gè)字節(jié)初始化為0。
calloc函數(shù)與malloc函數(shù)不同點(diǎn)在于:會(huì)將開辟的空間都初始化為0(按字節(jié)初始化為0)。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> int main() { int* p = (int*)calloc(10, sizeof(int)); if (p == NULL) { printf("%s\n", strerror(errno)); return -1; } for (int i = 0; i < 10; i++) { printf("%d ", p[i]); } printf("\n"); return 0; }
下面看運(yùn)行結(jié)果都為0
realloc函數(shù)
realloc函數(shù)讓動(dòng)態(tài)內(nèi)存管理變得更加靈活,空間不夠時(shí)可以對(duì)動(dòng)態(tài)開辟的空間擴(kuò)容
void* realloc(void* ptr, size_t szie);
參數(shù)ptr為需要擴(kuò)充動(dòng)態(tài)內(nèi)存分配的空間的地址
size是 調(diào)整之后新大小,返回參數(shù)為調(diào)整之后的內(nèi)存起始位置。
realloc在調(diào)整內(nèi)存空間的是存在兩種情況:
1.原地?cái)U(kuò)容
在需要擴(kuò)容的空間后有足夠的空間進(jìn)行擴(kuò)容,要擴(kuò)展內(nèi)存就直接原有內(nèi)存之后直接追加空間,原來空間的數(shù)據(jù)不發(fā)生變化。
2.異地?cái)U(kuò)容
原有空間之后沒有足夠多的空間時(shí),擴(kuò)展的方法是:在堆空間上另找一個(gè)合適大小 的連續(xù)空間來使用。這樣函數(shù)返回的是一個(gè)新的內(nèi)存地址。
pc指向的空間的數(shù)據(jù)會(huì)遷移到ptr指向的空間,此時(shí)不需要將pc的空間free,系統(tǒng)會(huì)自動(dòng)釋放。
下圖通過兩行代碼了解什么是異地?cái)U(kuò)容,可以看到p1和p2的地址發(fā)生了變化
p1的值是0x006b56b8 p2的值是0x006bf4d0,所以擴(kuò)容后的空間地址與原空間地址不同,這就是異地?cái)U(kuò)容
當(dāng)然擴(kuò)容失敗就會(huì)返回NULL,異常退出了
下面是malloc和realloc的聯(lián)合使用,我們擴(kuò)容后要將擴(kuò)容后的空間再次交給原指針去維護(hù)
所以將p=ptr
#include <stdio.h> #include <stdlib.h> int main() { int* p =(int*) malloc(sizeof(int)*10); if (p == NULL) { printf("malloc faied\n"); return -1; } int* ptr = (int*)realloc(p, sizeof(int) * 20); if (ptr == NULL) return -1; p = ptr; for (int j = 0; j < 20; j++) { printf("%d ", p[j]); } printf("\n"); return 0; }
運(yùn)行結(jié)果可以看到都是隨機(jī)數(shù),所以realloc函數(shù)也不會(huì)初始化
可以看出realloc與malloc相似,都不會(huì)初始化。同時(shí)這也是realloc和malloc的一個(gè)特性,
當(dāng)要擴(kuò)容的對(duì)象為空時(shí),realloc可以當(dāng)作malloc函數(shù)使用。
擴(kuò)充
我們一般動(dòng)態(tài)開辟的空間都是結(jié)構(gòu)體,下面簡(jiǎn)單介紹一下開辟結(jié)構(gòu)體類型的空間
我們首先定義了一個(gè)結(jié)構(gòu)體類型,里面定義的是int型的數(shù)據(jù)data,還有一個(gè)next型的指針,存放下一節(jié)點(diǎn)的地址,這就是數(shù)據(jù)結(jié)構(gòu)的單鏈表結(jié)構(gòu),不了解的小伙伴可以簡(jiǎn)單看一下
pc指向開辟的一個(gè)節(jié)點(diǎn)用,p指向開辟的另一個(gè)節(jié)點(diǎn)用,將pc指向的結(jié)構(gòu)體中的next保存p指向節(jié)點(diǎn)的地址
#include <stdio.h> #include <stdlib.h> typedef struct QueueNode { DataType data; struct QueueNode* next; }QueueNode; int main() { QueueNode* pc = (QueueNode*)malloc(sizeof(QueueNode)); QueueNode* p = (QueueNode*)malloc(sizeof(QueueNode)); if (pc == NULL && pc == NULL) { printf("malloc failde\n"); exit(-1); } pc->data = 10; pc->next = p; p->data = 20; p->next = NULL; printf("%d %d\n", pc->data, p->data); printf("%p %p %p\n", pc->next, p, p->next); free(pc); free(p); pc = p = NULL; return 0; }
重點(diǎn):動(dòng)態(tài)開辟使用完后,一定要記得free,置空。
了解完上面的內(nèi)容,是不是對(duì)動(dòng)態(tài)分配有了更深的理解。
malloc/calloc/realloc區(qū)別總結(jié)
相同點(diǎn):
1.都是從堆上申請(qǐng)空間
2.都需要對(duì)返回值判空
3.都需要用戶free釋放
4.返回值類型相同(void*)
5.都需要類型轉(zhuǎn)化
6.底層實(shí)現(xiàn)上是一樣的,都需要開辟多余的空間,用來維護(hù)申請(qǐng)的空間
可以輸入以下代碼觀測(cè)內(nèi)存:
#include <stdio.h> #include <malloc.h> int main(){ int *p= (int *)malloc(sizeof(int )*10); return 0; }
不同點(diǎn):
1.函數(shù)名字不同和參數(shù)類型不同。
2.calloc會(huì)對(duì)申請(qǐng)空間初始化,并且初始化為0,而其他兩個(gè)不會(huì)。
3.malloc申請(qǐng)的空間必須使用memset初始化
4.realloc是對(duì)已經(jīng)存在的空間進(jìn)行調(diào)整,當(dāng)?shù)谝粋€(gè)參數(shù)傳入NULL的時(shí)候和malloc一樣
總結(jié)
到此這篇關(guān)于C語言中動(dòng)態(tài)內(nèi)存分配malloc、calloc和realloc函數(shù)解析的文章就介紹到這了,更多相關(guān)C語言動(dòng)態(tài)內(nèi)存分配函數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言中字符串庫函數(shù)的實(shí)現(xiàn)及模擬
C語言中有很多數(shù)據(jù)類型,比如int(整數(shù)類型)、char(字符類型)、以及浮點(diǎn)型的double(雙精度)等。但是有一點(diǎn)就是我們發(fā)現(xiàn)這里并沒有提到我們常見的有關(guān)字符串的類型。本文為大家介紹了C語言中字符串庫函數(shù)的實(shí)現(xiàn)及模擬,需要的可以參考一下2022-11-11深入解析Radix Sort基數(shù)排序算法思想及C語言實(shí)現(xiàn)示例
基數(shù)排序和桶排序、計(jì)數(shù)排序共同是三種最常用的線性排序算法,這里我們就來深入解析Radix Sort基數(shù)排序算法思想及C語言實(shí)現(xiàn)示例,需要的朋友可以參考下2016-07-07C++知識(shí)點(diǎn)之成員函數(shù)中const的用法
這篇文章主要介紹了C++知識(shí)點(diǎn)之成員函數(shù)中const的用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11C++語言中std::array的用法小結(jié)(神器用法)
這篇文章主要介紹了C++語言中std::array的用法小結(jié),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11