最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C語言中常見的六種動態(tài)內(nèi)存錯(cuò)誤總結(jié)

 更新時(shí)間:2023年11月03日 10:47:44   作者:烽起黎明  
學(xué)習(xí)過C語言中的動態(tài)內(nèi)存函數(shù),例如【malloc】、【calloc】、【realloc】、【free】,那它們在使用的過程中會碰到哪些問題呢,本本文我們一起來探討下,感興趣的朋友跟著小編一起來看看吧

1、對NULL指針的解引用操作

代碼:

void test()
{
    int *p = (int *)malloc(INT_MAX/4);
    *p = 20;	//如果p的值是NULL,就會有問題
    free(p);
}

分析:

  • 首先看到第一個(gè),你要知道的是INT_MAX是什么。它是一個(gè)宏定義,表示int類型(整型)能夠表示的最大值,其值為2147483647,那在上面講malloc的時(shí)候我們有說到過,若是需要申請的空間過大的話可能就會導(dǎo)致申請失敗的問題,所以這里很致命的一個(gè)錯(cuò)誤就是在申請空間之后沒有去及時(shí)判斷是否申請成功
  • 可以看到編譯器也是給我們報(bào)出了一個(gè)Warning警告說:取消對NULL指針的引用

在這里插入圖片描述

改進(jìn):

  • 此時(shí)我們就可以對代碼去做一個(gè)改進(jìn),對malloc之后的返回值做一個(gè)判斷
void test()
{
    int* p = (int*)malloc(INT_MAX / 4);
    if (NULL == p)
    {
        perror("fail malloc");
        exit(-1);
    }
    *p = 20;//如果p的值是NULL,就會有問題
    free(p);
}
  • 這個(gè)時(shí)候我們就可以看到?jīng)]有警告再報(bào)出來了

在這里插入圖片描述

2、對動態(tài)開辟空間的越界訪問

代碼:

int main(void)
{
	int* p = (int*)malloc(100);
	if (NULL == p)
	{
		perror("malloc fail");
		exit(-1);
	}

	int i = 0;
	for (int i = 0; i < 100; i++)
	{
		*(p + i) = 0;	// 當(dāng)i == 25時(shí)便會越界
	}
	free(p);
	p = NULL;
	return 0;
}

分析:

  • 接下去我們來看這個(gè)越界訪問的問題,首先我們使用malloc向堆區(qū)申請了100個(gè)字節(jié)的空間,但是呢在下面對這塊空間進(jìn)行訪問的時(shí)候卻訪問了100個(gè)整型的大小,此時(shí)一定會造成訪問越界的問題
  • 但是呢口說無憑,我們一樣通過調(diào)試來進(jìn)行一個(gè)觀察,不過這里在進(jìn)行循環(huán)的時(shí)候i沒有到100的話是不會出問題的,所以為了方便調(diào)試我們需要去設(shè)置一個(gè)【條件斷點(diǎn)】,將i從【24】開始執(zhí)行,這樣我們很快就能觀察到結(jié)果了

在這里插入圖片描述

  • 然后我們便可以通過調(diào)試去進(jìn)行觀察了,可以看到i并沒有到達(dá)100,而是直接跳出了當(dāng)前循環(huán),然后在free()的時(shí)候就出現(xiàn)了問題,一般我們在一些其他地方觀察不到的問題就會在free()的地方顯現(xiàn)出來,因?yàn)榇藭r(shí)是要去釋放掉我們的這塊申請的空間了,便會引發(fā)一些異常

在這里插入圖片描述

其實(shí)我們可以將*(p + i) = 0修改成p[i] = 0,利用[]操作符對某個(gè)下標(biāo)進(jìn)行訪問,此時(shí)我們可以看到編譯器就報(bào)出了警告說索引"99"超出了“0"至”24"的有效范圍,因此100個(gè)字節(jié)的空間只能供25個(gè)整型來進(jìn)行存放,因此合法的下標(biāo)索引即為0 ~ 24

在這里插入圖片描述

改進(jìn):

  • 代碼修改這一塊的話我們只需要在申請空間的時(shí)候保證申請到足夠的、正確的容量即可
int* p = (int*)malloc(100 * sizeof(int));

這個(gè)時(shí)候我們就可以看到?jīng)]有警告再報(bào)出來了

在這里插入圖片描述

3、對非動態(tài)開辟內(nèi)存進(jìn)行free釋放

代碼:

void test()
{
	int a = 10;
	int* p = &a;
	free(p);	//ok?
}

分析:

  • 接下去再來看第三個(gè),這里是對非動態(tài)開辟的內(nèi)存進(jìn)行free()釋放,那我們在介紹free()的時(shí)候說到它只能釋放由【malloc】、【calloc】、【realloc】所開辟出來的空間,這些空間都是在堆區(qū)上進(jìn)行申請的,但是我們在普通的函數(shù)中所創(chuàng)建的普通變量無非是棧區(qū)或者靜態(tài)區(qū)的,它們的釋放工作并不是由free()來完成的,因此強(qiáng)行去這樣做的話就會造成了一個(gè)很大的問題
  • 可以看到一樣出現(xiàn)了我們剛才那樣類似的問題

在這里插入圖片描述

改進(jìn):

  • 本代碼并沒有什么通用的改進(jìn)辦法,如果不想出現(xiàn)問題的話就不要free()普通棧區(qū)上的變量即可,或者按照常規(guī)去動態(tài)申請然后在進(jìn)行free()

4、使用free釋放一塊動態(tài)開辟內(nèi)存的一部分

代碼:

void test()
{
    int* p = (int*)malloc(100);
    if (NULL == p)
    {
    	perror("malloc fail");
    	exit(-1);
    }
    for (int i = 0; i < 10; i++)
    {
        p++;
    }
    free(p);    //p不再指向動態(tài)內(nèi)存的起始位置
}

分析:

  • 本題的情境是這樣的,我們在堆區(qū)申請了100個(gè)字節(jié)后,讓指針p指向這塊地址的起始位置,然后讓其偏移了10個(gè)整型的位置,即40B的大小,那么此時(shí)指針p其實(shí)就指向了當(dāng)前這一塊地址的中間位置,那么此時(shí)再去free的時(shí)候其實(shí)就會出問題
  • 因?yàn)樵摵瘮?shù)在釋放動態(tài)申請的內(nèi)存時(shí)需要從這塊地址其實(shí)位置開始,然后釋放制定的字節(jié)數(shù),若是從某個(gè)中間位置開始的話就不對了

從下圖可以看出,因?yàn)?code>free()函數(shù)需要做到申請多少釋放多少,所以當(dāng)其釋放了一部分之后,就不夠了,便造成了訪問內(nèi)存錯(cuò)誤的問題

在這里插入圖片描述

  • 一樣,我們通過調(diào)試去進(jìn)行觀察,首先在一開始申請出這塊空間的時(shí)候先記錄一下初始位置的地址,然后我們便可以觀察到其進(jìn)行了一個(gè)偏移,

在這里插入圖片描述

  • 可以看到,此時(shí)若是去free()的話就會出現(xiàn)警告,很明顯這個(gè)debug_heap.cpp就是【堆】這一塊出的問題

在這里插入圖片描述

改進(jìn):

  • 要如何改進(jìn)的話就會不要去free()一塊動態(tài)開辟出來內(nèi)存的一部分,而是要從起始地址開始釋放,申請多少釋放多少

5、對同一塊動態(tài)內(nèi)存多次釋放

代碼:

void test()
{
    int* p = (int*)malloc(100);
    //使用...
    free(p);

    //...
    free(p);	//重復(fù)釋放
}

分析:

  • 這一點(diǎn)的話就是在我們釋放完一塊內(nèi)存空間后忘了,然后再去對其進(jìn)行了一次釋放,這種操作的話其實(shí)也是很危險(xiǎn)的,當(dāng)我們在第一次釋放的時(shí)候p所指向的那塊空間的使用權(quán)已經(jīng)還給操作系統(tǒng)了,但是呢我們并沒有對這個(gè)指針p做置空的操作,于是它還指向那塊空間所在的地址,不過里面的內(nèi)容已經(jīng)是隨機(jī)的了,那么這個(gè)指針就是一個(gè)【野指針
  • 此時(shí)再對其做一個(gè)free()的操作,就會造成操作野指針的問題

在這里插入圖片描述

改進(jìn):

  • 此時(shí)我們就可以對代碼去做一個(gè)簡單的改進(jìn),在第一次free后將指針p置為NULL即可,此刻若是后面再去free的話,就不會出現(xiàn)問題了,因?yàn)楫?dāng)我們傳遞NULL作為參數(shù)的時(shí)候,free(NULL)便不會去做任何的事情
void test()
{
    int* p = (int*)malloc(100);
    //使用...
    free(p);
    p = NULL;   // 將不使用的指針置為NULL
    //...
    free(p);	//重復(fù)釋放
}

6、動態(tài)開辟內(nèi)存忘記釋放(內(nèi)存泄漏)

代碼:

void test()
{
	int* p = (int*)malloc(100);
	if (NULL != p)
	{
		*p = 20;
	}
}

int main()
{
	test();
}

分析:

  • 那最后一個(gè)呢就是我們最常見的,在動態(tài)開辟內(nèi)存后忘記去釋放了,例如上面有一個(gè)test()函數(shù),函數(shù)內(nèi)部去申請了100個(gè)字節(jié)的數(shù)據(jù),并為其做了一個(gè)初始化,此時(shí)main函數(shù)就正常地去調(diào)用它,但是呢這中間卻沒有任何地free()釋放操作,就會存在【內(nèi)存泄漏】的問題

?? 那有同學(xué)說:既然函數(shù)內(nèi)部沒有做釋放的話我在調(diào)用結(jié)束后去free一下這個(gè)p不就好了

這句話其實(shí)就存在很大的問題,、對于一個(gè)在一個(gè)函數(shù)創(chuàng)建的變量,是處在當(dāng)前這個(gè)函數(shù)所維護(hù)的棧幀中的,所以當(dāng)這個(gè)函數(shù)調(diào)用結(jié)束后局部變量就會隨著棧幀的銷毀而不復(fù)存在,那此時(shí)我們再想去free()釋放這塊空間的時(shí)候,是無法訪問到這個(gè)指針p的。因此要釋放的話只能在函數(shù)內(nèi)部進(jìn)行才可以

改進(jìn):

  • 那改進(jìn)這一塊的話我們只需要在函數(shù)調(diào)用結(jié)束前去將其釋放即可,不過別忘了在free()之后要將指針置為NULL防止野指針
void test()
{
	int* p = (int*)malloc(100);
	if (NULL != p)
	{
		*p = 20;
	}
	
	free(p);
	p = NULL;
}
  • 所以當(dāng)我們在使用動態(tài)內(nèi)存的時(shí)候,一定要保證在【malloc】之后及時(shí)【free】,此時(shí)才能保證不會內(nèi)存泄漏

但是它們兩個(gè)成對出現(xiàn)就一定不會出現(xiàn)問題嗎?

  • 我們來看看下面這段代碼,可以看到中間有一個(gè)if(1)的條件判斷,我們知道這個(gè)條件是天然成立的,然后看到當(dāng)這個(gè)條件成立后就會執(zhí)行return語句,那么當(dāng)前這個(gè)函數(shù)就會結(jié)束了,此時(shí)并沒有運(yùn)行到free(p)這句話
  • 那么聰明的你一定很快反應(yīng)過來了,即使是存在【malloc】和【free】成對出現(xiàn)的情況下,可能也無法百分百保證不會產(chǎn)生內(nèi)存泄漏的問題,所以還是需要我們在寫程序的時(shí)候多注意細(xì)節(jié)
void test()
{
	int* p = (int*)malloc(100);
	if (NULL != p)
	{
		*p = 20;
	}
	if (1)
		return;		// 因?yàn)槟承l件中途return了, 沒到free()

	free(p);
}

int main()
{
	test();
}

總結(jié)與提煉

最后來總結(jié)一下本文所學(xué)習(xí)的內(nèi)容

  • 通過上面的六個(gè)案例,我們總共了解到了六種動態(tài)內(nèi)存錯(cuò)誤的形式,分別是
    • 對NULL指針的解引用操作】 —— 操作空指針是非常危險(xiǎn)的一件事,記得判空哦
    • 對動態(tài)開辟空間的越界訪問】 —— 有多少就拿多少,不要貪心哦
    • 對非動態(tài)開辟內(nèi)存進(jìn)行free釋放】 —— 請正確分類,送它去該去的地方
    • 使用free釋放一塊動態(tài)開辟內(nèi)存的一部分 】—— 借了多少還多少,不要私藏哦
    • 對同一塊動態(tài)內(nèi)存多次釋放】 —— 借了多少還多少,不要私藏哦
    • 動態(tài)開辟內(nèi)存忘記釋放】 —— 借了別人的東西要記得還
  • 在使用動態(tài)內(nèi)存函數(shù)開辟出空間后,使用的時(shí)候一定要牢記以上幾點(diǎn),否則要出大問題的!

以上就是C語言中常見的六種動態(tài)內(nèi)存錯(cuò)誤總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于C語言動態(tài)內(nèi)存錯(cuò)誤的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • VisualStudio2022提交git代碼的方法實(shí)現(xiàn)

    VisualStudio2022提交git代碼的方法實(shí)現(xiàn)

    本文主要介紹了VisualStudio2022提交git代碼的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • linux下基于C語言的信號編程實(shí)例

    linux下基于C語言的信號編程實(shí)例

    這篇文章主要介紹了linux下基于C語言的信號編程,實(shí)例分析了信號量的基本使用技巧與相關(guān)概念,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-07-07
  • C語言編程數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)詳解小白篇

    C語言編程數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)詳解小白篇

    這篇文章主要介紹了數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ),非常適合初學(xué)數(shù)據(jù)結(jié)構(gòu)的小白,有需要的朋友可以借鑒參考下,希望可以有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2021-09-09
  • C++訪問std::variant類型數(shù)據(jù)的幾種方式小結(jié)

    C++訪問std::variant類型數(shù)據(jù)的幾種方式小結(jié)

    std::variant是?C++17中引入的一個(gè)新的類模板,提供了一種存儲不同類型的值的方式,本文主要介紹了C++訪問std::variant類型數(shù)據(jù)的幾種方式小結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-02-02
  • 成員初始化列表與構(gòu)造函數(shù)體中的區(qū)別詳細(xì)解析

    成員初始化列表與構(gòu)造函數(shù)體中的區(qū)別詳細(xì)解析

    無論是在構(gòu)造函數(shù)初始化列表中初始化成員,還是在構(gòu)造函數(shù)體中對它們賦值,最終結(jié)果是相同的。不同之處在于,使用構(gòu)造函數(shù)初始化列表的版本初始化數(shù)據(jù)成員,沒有定義初始化列表的構(gòu)造函數(shù)版本在構(gòu)造函數(shù)體中對數(shù)據(jù)成員賦值
    2013-09-09
  • Matlab繪制花里胡哨的山脊圖

    Matlab繪制花里胡哨的山脊圖

    這篇文章主要介紹了如何利用Matlab實(shí)現(xiàn)繪制一些花里胡哨的山脊圖,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Matlab有一定的幫助,需要的可以參考一下
    2023-02-02
  • C語言srand函數(shù)的詳細(xì)用法示例小結(jié)

    C語言srand函數(shù)的詳細(xì)用法示例小結(jié)

    srand函數(shù)用于初始化隨機(jī)數(shù)生成器,為后續(xù)rand函數(shù)生成隨機(jī)數(shù)序列設(shè)置"種子",本文將從函數(shù)定義、工作原理、使用方法到注意事項(xiàng),全面講解srand函數(shù)的用法,感興趣的朋友跟隨小編一起看看吧
    2025-08-08
  • C語言strlen和sizeof在數(shù)組中的使用詳解

    C語言strlen和sizeof在數(shù)組中的使用詳解

    對于 strlen 和 sizeof,相信不少程序員會混淆其功能。雖然從表面上看它們都可以求字符串的長度,但二者卻存在著許多不同之處及本質(zhì)區(qū)別
    2021-10-10
  • 關(guān)于C++函數(shù)模版的實(shí)現(xiàn)講解

    關(guān)于C++函數(shù)模版的實(shí)現(xiàn)講解

    今天小編就為大家分享一篇關(guān)于關(guān)于C++函數(shù)模版的實(shí)現(xiàn)講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • C語言之預(yù)處理命令的深入講解

    C語言之預(yù)處理命令的深入講解

    這篇文章主要給大家介紹了關(guān)于C語言之預(yù)處理命令的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04

最新評論

特克斯县| 泾源县| 海兴县| 竹溪县| 富阳市| 浑源县| 高陵县| 镇康县| 通城县| 米林县| 金华市| 原平市| 永定县| 子洲县| 邹平县| 大新县| 林周县| 高青县| 佛冈县| 洛浦县| 太谷县| 宁国市| 昭通市| 澜沧| 西林县| 玛纳斯县| 牙克石市| 全州县| 北流市| 九寨沟县| 岗巴县| 枣强县| 静海县| 昌平区| 承德县| 刚察县| 玛沁县| 昂仁县| 汉寿县| 寿光市| 虞城县|