深入了解C語(yǔ)言的動(dòng)態(tài)內(nèi)存管理
一、為什么會(huì)存在動(dòng)態(tài)內(nèi)存
int data=20;//在棧空間上開(kāi)辟4個(gè)字節(jié)空間
char ch[5]={0};//在棧開(kāi)辟5個(gè)字節(jié)連續(xù)空間
上面展示的即為我們正常開(kāi)辟固定的內(nèi)存空間,它有兩個(gè)方面的特點(diǎn)
1.內(nèi)存空間所占大小是固定的,不能改變的。
2.創(chuàng)建數(shù)組時(shí),必須指明長(zhǎng)度大小,在編譯時(shí)內(nèi)存進(jìn)行分配。
很顯然靜態(tài)分配內(nèi)存分配在一些場(chǎng)景,就暴露出它的弊端。如果在開(kāi)發(fā)之前,我們不知道空間的需求,我們有時(shí)只有在程序運(yùn)行的時(shí)候才能知道自己所需要空間大小,這時(shí)候我們只能使用動(dòng)態(tài)分配內(nèi)存了。
二、動(dòng)態(tài)內(nèi)存函數(shù)
1.malloc和free

malloc函數(shù)的參數(shù)只有一個(gè)size_t size,向內(nèi)存申請(qǐng)一塊連續(xù)可用的空間,有幾點(diǎn)需要注意
1.如果開(kāi)辟成功的話(huà),返回指向開(kāi)辟好空間的指針
2.如果開(kāi)辟失敗的話(huà),則返回NULL,因此每次開(kāi)辟空間之后,都要進(jìn)行檢查
3.malloc函數(shù)未定義返回類(lèi)型,一切由使用者自己使用
4.需引用stdlib.h頭文件

free函數(shù)是和malloc配套使用的,每次在堆開(kāi)辟動(dòng)態(tài)空間后,程序結(jié)束之前,必須進(jìn)行空間釋放,不然會(huì)出現(xiàn)動(dòng)態(tài)空間泄露,在使用free時(shí),仍需要注意幾點(diǎn)
1.如果指針指向的空間不是動(dòng)態(tài)開(kāi)辟的,不能用free進(jìn)行釋放
2.如果指針指向的是null指針,則free函數(shù)什么事都不做
3.free不能多次使用
4.需引用stdlib.h頭文件
代碼如下(示例):
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
int main()
{
int* src = NULL;
src = (int*)malloc(40);//開(kāi)辟40字節(jié)動(dòng)態(tài)內(nèi)存
if (src == NULL)
{
printf("%s", strerror(errno));
return 1;
}
free(src);//進(jìn)行動(dòng)態(tài)內(nèi)存釋放
src = NULL;
return 0;
}
相信有人會(huì)問(wèn),不是已經(jīng)對(duì)動(dòng)態(tài)內(nèi)存進(jìn)行釋放,為什么還要令指針等于NULL,我們調(diào)試一把。


這里我們可以發(fā)現(xiàn),雖然動(dòng)態(tài)內(nèi)存進(jìn)行free釋放,但指針仍然指向被釋放的動(dòng)態(tài)內(nèi)存的地址,如果不置空,就會(huì)造成野指針,非法訪(fǎng)問(wèn)的問(wèn)題。
2.calloc

calloc和malloc最大的區(qū)別就是,malloc只負(fù)責(zé)對(duì)內(nèi)存進(jìn)行動(dòng)態(tài)開(kāi)辟,但calloc不僅開(kāi)辟,還進(jìn)行初始化。
代碼如下(示例):
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
int main()
{
int* src = (int*)calloc(10, sizeof(int));
if (src == NULL)
{
printf("%s", strerror(errno));
return 1;
}
free(src);//進(jìn)行動(dòng)態(tài)內(nèi)存釋放
src = NULL;
return 0;
}

我們調(diào)試一把可以發(fā)現(xiàn),calloc在開(kāi)辟空間時(shí)同時(shí)進(jìn)行了初始化。所以如何我們對(duì)申請(qǐng)的內(nèi)存空間的內(nèi)容要求初始化,那么可以很方便的使用calloc函數(shù)來(lái)完成任務(wù)。
3.realloc

當(dāng)我們一次開(kāi)辟動(dòng)態(tài)內(nèi)存不夠大的時(shí)候,realloc讓動(dòng)態(tài)內(nèi)存更加的靈活。realloc幾個(gè)參數(shù):
1.第一個(gè)參數(shù)為要調(diào)整內(nèi)存的地址
2.調(diào)整后大小
3.調(diào)整后內(nèi)存的起始位置
為什么還要返回調(diào)整后內(nèi)存的地址,不是直接就開(kāi)辟好了嗎?其實(shí)reallloc函數(shù)在開(kāi)辟時(shí)有以下兩種情況:
1.原來(lái)的內(nèi)存之后空間是足夠的,則直接開(kāi)辟
2.原來(lái)的內(nèi)存之后空間不夠用。
我們畫(huà)圖刨析一下


情況1:直接追加空間,原來(lái)數(shù)據(jù)不變
情況2:沒(méi)有足夠的空間,在堆上找一個(gè)大小合適的連續(xù)空間。所以函數(shù)返回的是一個(gè)新的內(nèi)存地址。
代碼如下(示例)
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
int main()
{
int* src = NULL;
src = (int*)malloc(40);//開(kāi)辟40字節(jié)動(dòng)態(tài)內(nèi)存
if (src == NULL)
{
printf("%s", strerror(errno));
return 1;
}
src = realloc(src, 80);
if (src == NULL)
{
printf("%s", strerror(errno));
return 1;
}
free(src);//進(jìn)行動(dòng)態(tài)內(nèi)存釋放
src = NULL;
return 0;
}
三、動(dòng)態(tài)內(nèi)存函數(shù)常見(jiàn)錯(cuò)誤
1.動(dòng)態(tài)內(nèi)存越界訪(fǎng)問(wèn)
void test1()
{
int* src = (int*)malloc(20);
if (NULL == src)
{
return 1;
}
int i = 0;
for (i = 0; i < 6; i++)
{
(*src+i)=i
}
free(src);
src = NULL;
}

在這里我們malloc只開(kāi)辟了20個(gè)字節(jié),但(*src+5)造成了越界訪(fǎng)問(wèn)
2.對(duì)NULL指針進(jìn)行解引用操作
void test2()
{
int* src = (int*)malloc(INT_MAX);//此處INT_MAX為int的最大值
*src = 10;//如果src是NULL時(shí),無(wú)法解引用
free(src);
return 0;
}
這里未對(duì)開(kāi)辟的動(dòng)態(tài)內(nèi)存空間進(jìn)行是否為空的判斷,當(dāng)為空時(shí),解引用就會(huì)出現(xiàn)錯(cuò)誤。
3.使用free釋放一塊動(dòng)態(tài)開(kāi)辟內(nèi)存的一部分
void test3()
{
int* src = (int*)malloc(40);
int i=0;
for(i=0;i<6;i++)
{
*(src+i)=i;
src++;
}
free(src);//此時(shí)src不指向起始位置
}

因?yàn)橹羔樦赶虻牡刂钒l(fā)生變化,不在指向起始未知,進(jìn)行free釋放是非常危險(xiǎn)的。
4.對(duì)靜態(tài)內(nèi)存進(jìn)行free釋放
void test4()
{
int a = 20;
int* src = &a;
free(src);
}
5.對(duì)同一內(nèi)存空間多次釋放
void test5()
{
int* src = (int*)malloc(40);
free(src);
free(src);//多次釋放
}
第一個(gè)free已經(jīng)將堆空間的動(dòng)態(tài)內(nèi)存進(jìn)行釋放,此時(shí)src已經(jīng)是一個(gè)野指針,在進(jìn)行釋放是十分危險(xiǎn)的。
6.動(dòng)態(tài)開(kāi)辟空間忘記釋放
void test6()
{
int* src = (int*)malloc(40);
if (src != NULL)
{
}
while (1);
}
在開(kāi)辟動(dòng)態(tài)內(nèi)存之后,一直進(jìn)行while循環(huán),為進(jìn)行free釋放,會(huì)造成內(nèi)存泄漏。
四、經(jīng)典筆試題
1.筆試1
void test(char* src)
{
src = (char*)malloc(30);
}
int main()
{
char* src = NULL;
test(src);
strcpy(src, "wo yao jin da chang");
printf(src);
free(src);
}
這里會(huì)輸出wo yao jin da chang 嗎?


這里很明顯,src仍然是NULL,所以無(wú)法輸出wo yao jin da chang
2.筆試2
char* test()
{
char arr[] = "wo yao jin da chang";
return arr;
}
int main()
{
char* src = NULL;
src = tset();
printf(src);
return 0;
}
這里會(huì)輸出wo yao jin da chang 嗎?

這里test函數(shù)確實(shí)把字符串地址傳給了src,但是字符串是局部變量,當(dāng)函數(shù)執(zhí)行完之后,就銷(xiāo)毀了,所以src輸出的內(nèi)容是隨機(jī)的。
3.筆試3
void test()
{
char* src = (char*)malloc(50);
if (src != NULL)
{
strcpy(src, "wo yao jin da chang");
}
free(src);
if (src != NULL)
{
strcpy(src, "taijuanlebujinle");
printf(src);
}
}
這里會(huì)輸出taijuanlebujinle 嗎?

這里對(duì)動(dòng)態(tài)內(nèi)存釋放后,繼續(xù)進(jìn)行賦值,對(duì)野指針進(jìn)行了訪(fǎng)問(wèn)是錯(cuò)誤的。
總結(jié)
看到這里大家對(duì)動(dòng)態(tài)內(nèi)存管理已經(jīng)有了一定的認(rèn)識(shí),應(yīng)該特別注意這幾點(diǎn),在進(jìn)行動(dòng)態(tài)內(nèi)存開(kāi)辟之后進(jìn)行判斷是否為空,使用完后進(jìn)行free釋放,并且置空,防止動(dòng)態(tài)內(nèi)存泄露,只要記住這幾點(diǎn)基本就可以很好的使用動(dòng)態(tài)內(nèi)存了。
以上就是深入了解C語(yǔ)言的動(dòng)態(tài)內(nèi)存管理的詳細(xì)內(nèi)容,更多關(guān)于C語(yǔ)言動(dòng)態(tài)內(nèi)存管理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- 詳解C語(yǔ)言中動(dòng)態(tài)內(nèi)存管理及柔性數(shù)組的使用
- C語(yǔ)言動(dòng)態(tài)內(nèi)存的分配最全面分析
- 一文帶你搞懂C語(yǔ)言動(dòng)態(tài)內(nèi)存管理
- 詳解C語(yǔ)言中的動(dòng)態(tài)內(nèi)存管理
- C語(yǔ)言動(dòng)態(tài)內(nèi)存分配圖文講解
- 使用c語(yǔ)言輕松實(shí)現(xiàn)動(dòng)態(tài)內(nèi)存管
- 一文帶你了解C語(yǔ)言中的動(dòng)態(tài)內(nèi)存管理函數(shù)
- C語(yǔ)言動(dòng)態(tài)內(nèi)存管理的原理及實(shí)現(xiàn)方法
- 詳解C語(yǔ)言中動(dòng)態(tài)內(nèi)存管理
- C語(yǔ)言中常見(jiàn)的六種動(dòng)態(tài)內(nèi)存錯(cuò)誤總結(jié)
- 一文解析C語(yǔ)言中動(dòng)態(tài)內(nèi)存管理
- C語(yǔ)言動(dòng)態(tài)內(nèi)存管理的實(shí)現(xiàn)示例
相關(guān)文章
教你如何使用C++ 統(tǒng)計(jì)地鐵中站名出現(xiàn)的字的個(gè)數(shù)
通過(guò)本文教大家如何使用C++ 統(tǒng)計(jì)地鐵中站名出現(xiàn)的字的個(gè)數(shù),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2022-01-01
OpenGL實(shí)現(xiàn)貝塞爾曲線(xiàn)或曲面
這篇文章主要為大家詳細(xì)介紹了OpenGL實(shí)現(xiàn)貝塞爾曲線(xiàn)或曲面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04
C 語(yǔ)言基礎(chǔ)教程(我的C之旅開(kāi)始了)[七]
C 語(yǔ)言基礎(chǔ)教程(我的C之旅開(kāi)始了)[七]...2007-02-02
Qt5 串口類(lèi)QSerialPort的實(shí)現(xiàn)
在Qt5以上提供了QtSerialPort模塊,方便編程人員快速的開(kāi)發(fā)應(yīng)用串口的應(yīng)用程序。本文主要介紹了Qt5 串口類(lèi)QSerialPort的實(shí)現(xiàn),,感興趣的可以了解一下2022-05-05
c++遞歸實(shí)現(xiàn)n皇后問(wèn)題代碼(八皇后問(wèn)題)
c++遞歸實(shí)現(xiàn)n皇后問(wèn)題代碼分享,大家參考使用吧2013-12-12
C語(yǔ)言字符串旋轉(zhuǎn)問(wèn)題的深入講解
這篇文章主要給大家介紹了關(guān)于C語(yǔ)言字符串旋轉(zhuǎn)問(wèn)題的相關(guān)資料,文中給出了詳細(xì)的實(shí)現(xiàn)方法,并對(duì)每種方法進(jìn)行了分析和示例代碼,需要的朋友可以參考下2021-09-09
C++實(shí)現(xiàn)LeetCode(131.拆分回文串)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(131.拆分回文串),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)與算法之單鏈表
單鏈表是一種鏈?zhǔn)酱嫒〉臄?shù)據(jù)結(jié)構(gòu),用一組地址任意的存儲(chǔ)單元存放線(xiàn)性表中的數(shù)據(jù)元素。本文將為大家介紹C語(yǔ)言中單鏈表的基本概念與讀取數(shù)據(jù)元素,需要的可以參考一下2021-12-12

