亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C語(yǔ)言編程動(dòng)態(tài)內(nèi)存分配常見(jiàn)錯(cuò)誤全面分析

 更新時(shí)間:2021年10月22日 11:38:42   作者:高郵吳少  
這篇文章主要介紹了C語(yǔ)言編程中動(dòng)態(tài)內(nèi)存分配的常見(jiàn)錯(cuò)誤全面分析講解,同樣遇到過(guò)C語(yǔ)言動(dòng)態(tài)內(nèi)存分配各種問(wèn)題的同學(xué)可以借鑒參考下,希望能夠有所幫助

前言:為什么存在動(dòng)態(tài)內(nèi)存分配?

我們已經(jīng)掌握的內(nèi)存開(kāi)辟方式如下

int a=10;//在??臻g上開(kāi)辟4字節(jié)
char arr[10]={0};//在??臻g上開(kāi)辟10字節(jié)連續(xù)空間

以上開(kāi)辟空間的方法有兩個(gè)缺點(diǎn):
1.空間開(kāi)辟的大小是固定的。
2.數(shù)組在聲明的時(shí)候,必須指定數(shù)組長(zhǎng)度,它所需要的內(nèi)存在編譯時(shí)進(jìn)行分配。
但是對(duì)于空間的需求,不僅僅是上述的情況。有時(shí)我們需要的空間大小在程序運(yùn)行時(shí)才能知道,這時(shí)上述的方法就不能滿足需要了,我們來(lái)介紹一種解決方案:動(dòng)態(tài)內(nèi)存分配

一、動(dòng)態(tài)內(nèi)存函數(shù)

在這里插入圖片描述

(圖片來(lái)自比特就業(yè)課)

計(jì)算機(jī)在使用時(shí)會(huì)有三個(gè)區(qū):常見(jiàn)的有棧區(qū)——用來(lái)存放局部變量、函數(shù)形式參數(shù);靜態(tài)區(qū)——用來(lái)存放靜態(tài)區(qū)和全局變量;最后一個(gè)堆區(qū)則是我們用來(lái)動(dòng)態(tài)內(nèi)存分配的,學(xué)習(xí)動(dòng)態(tài)內(nèi)存分配必須掌握以下4種函數(shù):

1.malloc和free函數(shù)

malloc函數(shù)聲明:

void*malloc(size_t size);//size_t即unsigned int

該函數(shù)向內(nèi)存申請(qǐng)一塊連續(xù)可用的空間,并返回指向這塊空間的指針。注意點(diǎn)如下:
1.如果開(kāi)辟成功,返回一個(gè)指向開(kāi)辟空間的指針
2.如果開(kāi)辟失敗,返回一個(gè)NULL指針
(由于是NULL指針,所以對(duì)于malloc函數(shù)常用assert進(jìn)行檢查)
3.返回值類(lèi)型為void*,所以malloc函數(shù)開(kāi)辟空間的類(lèi)型需要使用者自己進(jìn)行決定
4.如果size=0,malloc的行為取決具體編譯器

free函數(shù)聲明

void free(void*ptr);

free函數(shù)用來(lái)釋放動(dòng)態(tài)開(kāi)辟的空間,注意點(diǎn)如下:
1.如果參數(shù)ptr指向的空間不是動(dòng)態(tài)開(kāi)辟的,那free函數(shù)的行為是未知的
2.如果ptr是空指針,free函數(shù)什么也不做
實(shí)戰(zhàn)舉例:

#include<stdio.h>
#include<stdlib.h>//malloc和free函數(shù)頭文件
int main()
{
	int*p=(int*)malloc(40);//malloc出來(lái)的空間是不確定類(lèi)型的,需要你自己強(qiáng)轉(zhuǎn)一個(gè)類(lèi)型
	//這里我們把它轉(zhuǎn)換成int*,由整型指針對(duì)這塊空間進(jìn)行維護(hù)
	//那這里會(huì)是4個(gè)字節(jié)看成一個(gè)元素,40個(gè)字節(jié),可填充10個(gè)int元素
	if (p == NULL)
	{
		return -1;
	}//如果沒(méi)有返回值則說(shuō)明開(kāi)辟成功
	int i = 0;
	for (i = 0;i < 10;i++)
	{
		*(p + i)=i;//對(duì)開(kāi)辟空間進(jìn)行賦值操作
	}
	free(p);//用完之后不需要了,用free函數(shù)釋放p所指向的空間(40個(gè)字節(jié)全部釋放掉,還給操作系統(tǒng))
	p = NULL;//由于p存儲(chǔ)的是開(kāi)辟空間的地址,即使空間還給了操作形態(tài),但p還是可以找到這塊空間,這是非常危險(xiǎn)的,所以我們用一個(gè)空指針賦給p
	return 0;
}

2.calloc函數(shù)

calloc函數(shù)也是用來(lái)動(dòng)態(tài)內(nèi)存分配的,函數(shù)聲明如下

void*calloc(size_t num,size_t size)

該函數(shù)功能是為num個(gè)大小為size的元素開(kāi)辟一塊空間,并且把空間的每個(gè)字節(jié)都初始化為0
它與malloc函數(shù)的區(qū)別只是在返回地址前會(huì)把申請(qǐng)空間里的每個(gè)字節(jié)都初始化為0

代碼如下(示例):

#include<stdio.h>
#include<stdlib.h>//calloc函數(shù)頭文件
#include<string.h>//strerror函數(shù)頭文件
#include<errno.h>//errno頭文件
int main()
{
	int*p = (int*)calloc(10, sizeof(int));//開(kāi)辟10個(gè)大小為int型的空間
	if (p == NULL)//calloc函數(shù)也有可能開(kāi)辟空間失敗,需要進(jìn)行檢驗(yàn)
	{
		printf("%s\n", strerror(errno));//errno是錯(cuò)誤碼,strerror會(huì)把錯(cuò)誤碼轉(zhuǎn)換成相應(yīng)的錯(cuò)誤信息
		return -1;
	}
	int i = 0;
	for (i = 0;i < 10;i++)
	{
		printf("%d ", *(p + i));//會(huì)打印10個(gè)0(calloc會(huì)自己初始化為0)
	}
	free(p);
	p = NULL;
	return 0;
}

3.realloc函數(shù)

realloc函數(shù)是在已有空間不夠的情況下,進(jìn)行追加申請(qǐng)空間的函數(shù),其聲明如下:

void*realloc(void*memblock,size_t size);

realloc函數(shù)是為了讓動(dòng)態(tài)內(nèi)存管理更加靈活,有時(shí)候我們發(fā)現(xiàn)之前申請(qǐng)的空間過(guò)少了,或者過(guò)大了,我們可以用realloc函數(shù)來(lái)進(jìn)行增減,它的注意點(diǎn)如下:
1.ptr是要調(diào)整的內(nèi)存地址
2.size是調(diào)整之后新的大小
3.返回值是調(diào)整后的內(nèi)存起始位置
4.該函數(shù)在調(diào)整原先內(nèi)存大小的基礎(chǔ)上,還會(huì)將原來(lái)內(nèi)存的數(shù)據(jù)復(fù)制到新空間
5.realloc函數(shù)在調(diào)整內(nèi)存空間存在兩種情況
關(guān)于4和5解釋如下:
假設(shè)我們現(xiàn)在追加1倍空間

在這里插入圖片描述

第一種:如上圖,紅色是我們開(kāi)辟的空間,藍(lán)色是其他程序正在使用的空間,又因?yàn)槲覀円_(kāi)辟空間肯定是物理上連續(xù)的,我們又不能使用藍(lán)色部分,中間的空白空間又完全不夠原先空間的兩倍,那怎么辦?

在這里插入圖片描述

我們會(huì)另外找一塊足夠空間的地方進(jìn)行原空間兩倍的開(kāi)辟,并且把原空間內(nèi)數(shù)據(jù)進(jìn)行復(fù)制到新空間,函數(shù)返回新空間的地址。

第二種:這種就比較簡(jiǎn)單了,原先空間后面就足夠追加開(kāi)辟一塊1倍的空間,我們直接進(jìn)行開(kāi)辟即可。

在這里插入圖片描述

在這里插入圖片描述

函數(shù)直接會(huì)返回原先空間的首地址。
當(dāng)然了,realloc函數(shù)同malloc和calloc函數(shù)一樣,也有可能開(kāi)辟空間失敗,所以依然需要檢驗(yàn)是否返回的是空指針

#include<stdio.h>
#include<stdlib.h>//malloc和free函數(shù)頭文件
int main()
{
	int*p=(int*)malloc(40);//開(kāi)辟10個(gè)int大小的空間
	int i = 0;
	for (i = 0;i < 10;i++)
	{
		*(p + i)=i;//對(duì)開(kāi)辟空間進(jìn)行賦值操作
	}
	int*ptr = realloc(p, 20 * sizeof(int));//注意!這里20*sizeof(int)是新的大小
	//比如我現(xiàn)在只有10個(gè),我需要20個(gè),差10個(gè)。但這里不是寫(xiě)10,而是寫(xiě)新的大小20
	if (ptr != NULL)
	{
		p = ptr;
	}
	for (i = 10;i < 20;i++)//對(duì)新開(kāi)辟空間賦值
	{
		*(p + i) = i;
	}
	for (i = 0;i < 20;i++)
	{
		printf("%d ", *(p + i));//打印0-19
	}
	free(p);
	p = NULL;
	return 0;
}

二、常見(jiàn)錯(cuò)誤

1.對(duì)NULL指針解引用

代碼如下(示例):

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int*p = (int*)malloc(20);
	*p = 0;//對(duì)p這個(gè)地址解引用并賦值為0
	free(p);
	return 0;
}

malloc等等函數(shù)在開(kāi)辟空間時(shí)都是有可能開(kāi)辟失敗的,萬(wàn)一失敗,就是返回空指針,你直接對(duì)空指針解引用并賦值肯定是有問(wèn)題的

所以我們這里還是要進(jìn)行指針檢驗(yàn)
代碼如下(示例):

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int*p = (int*)malloc(20);
	if(p==NULL)
	{
	return -1;
	//如果返回,開(kāi)辟失敗結(jié)束程序,如果沒(méi)有返回則可進(jìn)行下面的操作
	}
	*p = 0;//對(duì)p這個(gè)地址解引用并賦值為0
	free(p);
	return 0;
}

2.對(duì)動(dòng)態(tài)開(kāi)辟空間的越界訪問(wèn)

代碼如下(示例):

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int*p = (int*)malloc(200);//200個(gè)字節(jié)也就是50個(gè)int型
	if (p == NULL)
	{
		return -1;
	}
	int i = 0;
	for (i = 0;i < 60;i++)
	{
		*(p + i) = i;
	}
	free(p);
	p = NULL;
	return 0;
}

這個(gè)代碼乍一看上去沒(méi)有問(wèn)題,但是仔細(xì)看的話就會(huì)發(fā)現(xiàn)端倪,malloc開(kāi)辟200字節(jié)空間也就是50個(gè)int型,你for循環(huán)賦值最多循環(huán)次數(shù)也只能是50次啊,你循環(huán)60次肯定是越界訪問(wèn)了,這里也是妥妥的會(huì)報(bào)錯(cuò)。

3.對(duì)非動(dòng)態(tài)開(kāi)辟使用free函數(shù)

代碼如下(示例):

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int a = 10;
	int*p = &a;
	free(p);
	p = NULL;
}

我們這里int創(chuàng)建了a,然后把a(bǔ)的地址賦給了int*類(lèi)型的p,再然后free掉p。這種操作也是鐵定會(huì)報(bào)錯(cuò)的,p這個(gè)局部變量是在棧上的,而free函數(shù)針對(duì)的是堆區(qū)

4.使用free釋放一塊動(dòng)態(tài)內(nèi)存開(kāi)辟內(nèi)存的一部分

代碼如下(示例):

//使用free釋放一塊動(dòng)態(tài)內(nèi)存開(kāi)辟內(nèi)存的一部分
#include<stdio.h>
#include<stdlib.h>
int main()
{
	int*p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		return -1;
	}
	//使用
	int i = 0;
	for (i = 0;i < 5;i++)
	{
		*p++ = i;
	}
	//釋放
	free(p);
	p = NULL;
}

這里的代碼有什么問(wèn)題呢?我們畫(huà)一個(gè)圖就一目了然了

在這里插入圖片描述

一開(kāi)始p在上圖位置,然而隨著for循環(huán),p++這個(gè)操作,p指向的位置不斷往后,一直到下圖位置

在這里插入圖片描述

這時(shí)p已經(jīng)不指向原先開(kāi)辟空間的位置了,你這時(shí)候去用free釋放掉顯然是不合適的

5.對(duì)同一塊空間多次釋放

我們先來(lái)看2段代碼:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int*p = (int*)malloc(40);
	if (p == NULL)
	{
		return -1;
	}
	free(p);
	free(p);
}
#include<stdio.h>
#include<stdlib.h>
int main()
{
	int*p = (int*)malloc(40);
	if (p == NULL)
	{
		return -1;
	}
	free(p);
	p = NULL;
	free(p);
}

兩段代碼都是對(duì)同一塊空間多次釋放,但第一段代碼會(huì)報(bào)錯(cuò),第二段不會(huì)。
解釋如下:
第一段代碼你已經(jīng)釋放掉p所指向的空間了,空間里什么也沒(méi)有了,但p仍然指向那塊空間,所以你再次釋放不屬于你的空間肯定會(huì)報(bào)錯(cuò)。
第二段代碼你釋放掉p所指向空間,然后用空指針給p賦值,再去釋放空指針,我們知道,free空指針是什么也不做,所以不會(huì)報(bào)錯(cuò)。

6.動(dòng)態(tài)開(kāi)辟內(nèi)存忘記釋放

對(duì)于動(dòng)態(tài)開(kāi)辟內(nèi)存忘記釋放,在堆區(qū)上申請(qǐng)的空間有2種回收方式:
1.你自己free掉
2.程序退出時(shí),系統(tǒng)自動(dòng)回收
我們先來(lái)看一段代碼

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int*p = (int*)malloc(40);
	if (p == NULL)
	{
		return -1;
	}
	getchar();
	return 0;
}

該代碼我們沒(méi)有自己使用free來(lái)釋放內(nèi)存,而中間又有g(shù)etchar一直在等待接收字符,打個(gè)比方:假如你中途去上廁所或者干其他事情了,getchar一直沒(méi)有接收到字符,程序就一直沒(méi)有結(jié)束,那我們用p開(kāi)辟的空間在你上廁所期間就一直被占用,那塊空間系統(tǒng)沒(méi)辦法去做別的有意義的事情。而上升到將來(lái)公司層面:我們寫(xiě)的程序可能一天24h都在跑,那遇到這種情況,你沒(méi)有free掉內(nèi)存,你不用又不回收,整體效率的影響是非常大的。

總結(jié)

今天介紹了動(dòng)態(tài)內(nèi)存分配函數(shù)和一些常見(jiàn)的動(dòng)態(tài)內(nèi)存分配的錯(cuò)誤,希望讀者學(xué)習(xí)有所收獲,祝讀者學(xué)業(yè)有成,萬(wàn)事順心!

更多關(guān)于C語(yǔ)言動(dòng)態(tài)內(nèi)存分配的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C語(yǔ)言中進(jìn)程間通訊的方式詳解

    C語(yǔ)言中進(jìn)程間通訊的方式詳解

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言中幾種進(jìn)程間通訊的方式,文中的示例代碼講解詳細(xì),?對(duì)我們學(xué)習(xí)或工作有一定的借鑒價(jià)值,需要的可以參考一下
    2022-08-08
  • C++ 非遞歸實(shí)現(xiàn)二叉樹(shù)的前中后序遍歷

    C++ 非遞歸實(shí)現(xiàn)二叉樹(shù)的前中后序遍歷

    本文將結(jié)合動(dòng)畫(huà)和代碼演示如何通過(guò)C++ 非遞歸實(shí)現(xiàn)二叉樹(shù)的前中后序的遍歷,代碼具有一定的價(jià)值,感興趣的同學(xué)可以學(xué)習(xí)一下
    2021-11-11
  • C語(yǔ)言實(shí)現(xiàn)實(shí)驗(yàn)設(shè)備管理系統(tǒng)

    C語(yǔ)言實(shí)現(xiàn)實(shí)驗(yàn)設(shè)備管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)實(shí)驗(yàn)設(shè)備管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • exec()函數(shù)在C++中的應(yīng)用及其用法

    exec()函數(shù)在C++中的應(yīng)用及其用法

    exec()函數(shù)在C++中是一個(gè)進(jìn)程控制函數(shù),用于創(chuàng)建新進(jìn)程執(zhí)行其他程序或命令行指令。exec()函數(shù)可以替換當(dāng)前進(jìn)程的代碼和數(shù)據(jù),創(chuàng)建新的進(jìn)程運(yùn)行其他程序。exec()函數(shù)有多個(gè)版本,例如execl、execv、execle、execve等,根據(jù)不同的參數(shù)類(lèi)型和個(gè)數(shù)來(lái)使用
    2023-05-05
  • C++中COM組件初始化方法實(shí)例分析

    C++中COM組件初始化方法實(shí)例分析

    這篇文章主要介紹了C++中COM組件初始化方法,涉及C++中COM組件的使用技巧,需要的朋友可以參考下
    2015-05-05
  • 美化你的代碼 vb(VBS)代碼格式化的實(shí)現(xiàn)代碼

    美化你的代碼 vb(VBS)代碼格式化的實(shí)現(xiàn)代碼

    雖然VB.NET出現(xiàn)很久了,但還有好多人仍然在使用VB6。我在實(shí)現(xiàn)一些小功能的時(shí)候也喜歡用VB6,畢竟誰(shuí)都不想每天的美好心情被VS那烏龜般的啟動(dòng)速度影響
    2012-05-05
  • c++基礎(chǔ)學(xué)習(xí)之如何區(qū)分引用和指針

    c++基礎(chǔ)學(xué)習(xí)之如何區(qū)分引用和指針

    C語(yǔ)言中只有指針,C++加入了引用,能夠起到跟指針類(lèi)似的作用,下面這篇文章主要給大家介紹了關(guān)于c++基礎(chǔ)學(xué)習(xí)之區(qū)分引用和指針的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-08-08
  • visual studio code 編譯運(yùn)行html css js文件的教程

    visual studio code 編譯運(yùn)行html css js文件的教程

    這篇文章主要介紹了visual studio code 如何編譯運(yùn)行html css js文件,本文通過(guò)圖文實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-03-03
  • C++實(shí)現(xiàn)中值濾波的示例代碼

    C++實(shí)現(xiàn)中值濾波的示例代碼

    本文主要介紹了C++實(shí)現(xiàn)中值濾波的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • C++深入探究引用的本質(zhì)與意義

    C++深入探究引用的本質(zhì)與意義

    引用是C++一個(gè)很重要的特性,顧名思義是某一個(gè)變量或?qū)ο蟮膭e名,對(duì)引用的操作與對(duì)其所綁定的變量或?qū)ο蟮牟僮魍耆葍r(jià),這篇文章主要給大家總結(jié)介紹了C++中引用的相關(guān)知識(shí)點(diǎn),需要的朋友可以參考下
    2022-04-04

最新評(píng)論