国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

C生萬(wàn)物 | 常見的六種動(dòng)態(tài)內(nèi)存錯(cuò)誤

這篇具有很好參考價(jià)值的文章主要介紹了C生萬(wàn)物 | 常見的六種動(dòng)態(tài)內(nèi)存錯(cuò)誤。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

C生萬(wàn)物 | 常見的六種動(dòng)態(tài)內(nèi)存錯(cuò)誤

學(xué)習(xí)過C語(yǔ)言中的動(dòng)態(tài)內(nèi)存函數(shù),例如【malloc】、【calloc】、【realloc】、【free】,那它們?cè)谑褂玫倪^程中會(huì)碰到哪些問題呢,本本文我們一起來探討下~

1、對(duì)NULL指針的解引用操作

代碼:

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

分析:

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

C生萬(wàn)物 | 常見的六種動(dòng)態(tài)內(nèi)存錯(cuò)誤
改進(jìn):

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

C生萬(wàn)物 | 常見的六種動(dòng)態(tài)內(nèi)存錯(cuò)誤

2、對(duì)動(dòng)態(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í)便會(huì)越界
	}
	free(p);
	p = NULL;
	return 0;
}

分析:

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

C生萬(wàn)物 | 常見的六種動(dòng)態(tài)內(nèi)存錯(cuò)誤

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

C生萬(wàn)物 | 常見的六種動(dòng)態(tài)內(nèi)存錯(cuò)誤

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

C生萬(wàn)物 | 常見的六種動(dòng)態(tài)內(nèi)存錯(cuò)誤

改進(jìn):

  • 代碼修改這一塊的話我們只需要在申請(qǐng)空間的時(shí)候保證申請(qǐng)到足夠的、正確的容量即可
int* p = (int*)malloc(100 * sizeof(int));
  • 這個(gè)時(shí)候我們就可以看到?jīng)]有警告再報(bào)出來了

C生萬(wàn)物 | 常見的六種動(dòng)態(tài)內(nèi)存錯(cuò)誤

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

代碼:

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

分析:

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

C生萬(wàn)物 | 常見的六種動(dòng)態(tài)內(nèi)存錯(cuò)誤

改進(jìn):

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

4、使用free釋放一塊動(dòng)態(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不再指向動(dòng)態(tài)內(nèi)存的起始位置
}

分析:

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

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

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

C生萬(wàn)物 | 常見的六種動(dòng)態(tài)內(nèi)存錯(cuò)誤

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

C生萬(wàn)物 | 常見的六種動(dòng)態(tài)內(nèi)存錯(cuò)誤

改進(jìn):

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

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

代碼:

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

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

分析:

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

C生萬(wàn)物 | 常見的六種動(dòng)態(tài)內(nèi)存錯(cuò)誤

改進(jìn):

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

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

代碼:

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

int main()
{
	test();
}

分析:

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

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

  • 這句話其實(shí)就存在很大的問題,如果讀者有看過我的函數(shù)棧幀一文的話,就會(huì)很清楚了,對(duì)于一個(gè)在一個(gè)函數(shù)創(chuàng)建的變量,是處在當(dāng)前這個(gè)函數(shù)所維護(hù)的棧幀中的,所以當(dāng)這個(gè)函數(shù)調(diào)用結(jié)束后局部變量就會(huì)隨著棧幀的銷毀而不復(fù)存在,那此時(shí)我們?cè)傧肴?code>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)我們?cè)谑褂脛?dòng)態(tài)內(nèi)存的時(shí)候,一定要保證在【malloc】之后及時(shí)【free】,此時(shí)才能保證不會(huì)內(nèi)存泄漏

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

  • 我們來看看下面這段代碼,可以看到中間有一個(gè)if(1)的條件判斷,我們知道這個(gè)條件是天然成立的,然后看到當(dāng)這個(gè)條件成立后就會(huì)執(zhí)行return語(yǔ)句,那么當(dāng)前這個(gè)函數(shù)就會(huì)結(jié)束了,此時(shí)并沒有運(yùn)行到free(p)這句話
  • 那么聰明的你一定很快反應(yīng)過來了,即使是存在【malloc】和【free】成對(duì)出現(xiàn)的情況下,可能也無法百分百保證不會(huì)產(chǎn)生內(nèi)存泄漏的問題,所以還是需要我們?cè)趯懗绦虻臅r(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è)案例,我們總共了解到了六種動(dòng)態(tài)內(nèi)存錯(cuò)誤的形式,分別是
    • 對(duì)NULL指針的解引用操作】 —— 操作空指針是非常危險(xiǎn)的一件事,記得判空哦
    • 對(duì)動(dòng)態(tài)開辟空間的越界訪問】 —— 有多少就拿多少,不要貪心哦
    • 對(duì)非動(dòng)態(tài)開辟內(nèi)存進(jìn)行free釋放】 —— 請(qǐng)正確分類,送它去該去的地方
    • 使用free釋放一塊動(dòng)態(tài)開辟內(nèi)存的一部分 】—— 借了多少還多少,不要私藏哦
    • 對(duì)同一塊動(dòng)態(tài)內(nèi)存多次釋放】 —— 借了多少還多少,不要私藏哦
    • 動(dòng)態(tài)開辟內(nèi)存忘記釋放】 —— 借了別人的東西要記得還
  • 在使用動(dòng)態(tài)內(nèi)存函數(shù)開辟出空間后,使用的時(shí)候一定要牢記以上幾點(diǎn),否則要出大問題的!

以上就是本文要介紹的所有內(nèi)容 ,感謝您的閱讀??

C生萬(wàn)物 | 常見的六種動(dòng)態(tài)內(nèi)存錯(cuò)誤文章來源地址http://www.zghlxwxcb.cn/news/detail-511998.html

到了這里,關(guān)于C生萬(wàn)物 | 常見的六種動(dòng)態(tài)內(nèi)存錯(cuò)誤的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 關(guān)于Bean的六種作用域

    在JavaSE中,我們學(xué)習(xí)過了全局變量以及局部變量,這里就涉及到了作用域問題,那么什么是作用域呢? 限定程序中變量的使用范圍叫做作用域,或者說在源代碼中定義變量的某個(gè)區(qū)域就叫做作用域。 而Bean的作用域指的是 Bean在Spring整個(gè)框架中的某種行為模式 , 比如singleto

    2024年02月08日
    瀏覽(26)
  • 【SpringMVC]獲取參數(shù)的六種方式

    【SpringMVC]獲取參數(shù)的六種方式

    目錄 1.通過ServletAPI獲取 2.通過控制器方法的形參獲取 3.@RequestParam:將請(qǐng)求參數(shù)和控制器方法的形參綁定 4.@RequestHeader:將請(qǐng)求頭信息與控制器方法的形參的值進(jìn)行綁定 5. CookieValue:將cookie數(shù)據(jù)和控制器方法的形參綁定 Cookie: ?編輯 6.通過控制器方法的實(shí)體類類型的形參獲取

    2024年02月09日
    瀏覽(20)
  • Python中的六種基本數(shù)據(jù)類型

    Python中分為六種基本數(shù)據(jù)類型 不可變類型(又叫靜態(tài)數(shù)據(jù)類型,沒有增刪改操作):數(shù)字(number)、字符串(string)、元組(tuple) 可變類型(又叫動(dòng)態(tài)數(shù)據(jù)類型,支持增刪改操作):列表(list)、字典(dictionary)、集合(set) 1. 數(shù)字類型(numbers): 數(shù)字類型下還可分為整數(shù)(int)、浮點(diǎn)數(shù)(f

    2024年02月04日
    瀏覽(25)
  • 斐波那契數(shù)列的六種解法

    做這個(gè)問題之前,我們需要了解到斐波那契數(shù)列是什么東西?是干什么的? 斐波那契數(shù)列是什么? 一、斐波那契數(shù)列指的是這樣一個(gè)數(shù)列:1、1、2、3、5、8、13、21、…… 這個(gè)數(shù)列從第三項(xiàng)開始,每一項(xiàng)都等于前兩項(xiàng)之和。 二、應(yīng)用:通常在個(gè)別股票中不是太準(zhǔn)確,通常在指數(shù)上

    2024年02月08日
    瀏覽(34)
  • SpringBoot 實(shí)現(xiàn)跨域的六種方式

    目錄 1.通過SpringSecurity方式配置 2.使用Spring提供的CorsFilter注入Bean(推薦) 3.使用注解@CrossOrigin注解(繁瑣) 4.通過ResponseBodyAdvice 實(shí)現(xiàn)跨域 5.通過HttpServletResponse設(shè)置跨域 6.通過WebMvcConfigurer 實(shí)現(xiàn)跨域 與第5類似

    2024年02月14日
    瀏覽(29)
  • uniapp路由跳轉(zhuǎn)的六種方式

    uniapp路由跳轉(zhuǎn)的六種方式

    uniapp官方文檔詳解: 一、uni.navigateTo保留當(dāng)前頁(yè)面,跳轉(zhuǎn)到應(yīng)用內(nèi)的某個(gè)頁(yè)面,使用uni.navigateBack可以返回到原頁(yè)面。 注意: 頁(yè)面跳轉(zhuǎn)路徑有層級(jí)限制,不能無限制跳轉(zhuǎn)新頁(yè)面 跳轉(zhuǎn)到 tabBar 頁(yè)面只能使用 switchTab 跳轉(zhuǎn) 二、uni.redirectTo關(guān)閉當(dāng)前頁(yè)面,跳轉(zhuǎn)到應(yīng)用內(nèi)的某個(gè)頁(yè)面。

    2024年02月11日
    瀏覽(31)
  • C語(yǔ)言實(shí)現(xiàn)排序算法的六種方式

    1、冒泡法 2、交換法 每次用當(dāng)前的元素一一的同其后的元素 3、選擇法 從數(shù)據(jù)中選擇最小的同第一個(gè)值交換,在從剩下的部分中選擇最小的與第二個(gè)交換,這樣往復(fù)下去 4、插入法 在前面的數(shù)中尋找相應(yīng)的位置插入, 然后繼續(xù)下一張 插入排序就是每一步都將一個(gè)待排數(shù)據(jù)按

    2024年01月25日
    瀏覽(18)
  • bitmap的六種壓縮方式,Android圖片壓縮

    bitmap的六種壓縮方式,Android圖片壓縮

    Android中圖片是以bitmap形式存在的,那么bitmap所占內(nèi)存,直接影響到了應(yīng)用所占內(nèi)存大小,首先要知道bitmap所占內(nèi)存大小計(jì)算方式: 圖片長(zhǎng)度 x 圖片寬度 x 一個(gè)像素點(diǎn)占用的字節(jié)數(shù) 以下是圖片的壓縮格式: 其中,A代表透明度;R代表紅色;G代表綠色;B代表藍(lán)色。 ALPHA_8 表示

    2024年02月09日
    瀏覽(30)
  • vue組件間傳值的六種方法

    vue組件間傳值的六種方法

    父組件代碼: 子組件代碼: 頁(yè)面顯示: 子組件代碼: 父組件代碼: 頁(yè)面顯示: 父子組件傳值原理:父子組件的關(guān)系可以總結(jié)為 prop 向下傳遞,事件向上傳遞。父組件通過 prop 給子組件下發(fā)數(shù)據(jù),子組件通過事件給父組件發(fā)送消息,如下圖所示: ref:如果在普通的 DOM 元素

    2024年02月15日
    瀏覽(30)
  • 【Vue學(xué)習(xí)筆記】跨域的六種解決方案

    @TOC 跨域問題指的是在瀏覽器端,當(dāng)一個(gè)網(wǎng)頁(yè)的腳本(如JavaScript)向另一個(gè)域名的網(wǎng)站發(fā)起請(qǐng)求時(shí),如果兩個(gè)網(wǎng)站的域名不一致,就會(huì)出現(xiàn)跨域問題。由于瀏覽器的同源策略(Same Origin Policy),默認(rèn)情況下,腳本只能訪問同一個(gè)域名下的資源,不能訪問其他域名下的資源。

    2024年02月10日
    瀏覽(90)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包