日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無先后,達(dá)者為師

網(wǎng)站首頁 編程語言 正文

關(guān)于C語言動態(tài)內(nèi)存管理介紹_C 語言

作者:invictusQAQ ? 更新時間: 2022-04-09 編程語言

1.為什么需要動態(tài)內(nèi)存分配

關(guān)于這個問題,我們先看看我們之前是如何開辟內(nèi)存的。

int val = 20;//在棧空間上開辟四個字節(jié)
char arr[10] = {0};//在棧空間上開辟10個字節(jié)的連續(xù)空間

但可以發(fā)現(xiàn)的一個問題是,無論我們怎樣開辟內(nèi)存空間,他的大小都在開辟前就已經(jīng)被指定,而顯然在實際應(yīng)用中并不是所有情況我們都能在程序編譯前就知道他需要多大的內(nèi)存空間。或許你想說那有備無患開大點不久好了?但這樣所造成的空間浪費(fèi)并不是我們所希望看到的結(jié)果。于是我們就只能試試動態(tài)內(nèi)存分配了。

2.有關(guān)動態(tài)內(nèi)存函數(shù)介紹

2.1 malloc和free

c語言已經(jīng)為我們提供了一個動態(tài)內(nèi)存開辟的函數(shù)malloc

void* malloc (size_t size);

1.這個函數(shù)向內(nèi)存申請一塊連續(xù)可用的空間,并返回指向這塊空間的指針。

2.如果開辟成功,則返回一個指向開辟好空間的指針。

3.如果開辟失敗,則返回一個NULL指針,因此malloc的返回值一定要做檢查。

4.返回值的類型是 void* ,所以malloc函數(shù)并不知道開辟空間的類型,具體在使用的時候使用者自 己來決定。

5.如果參數(shù) size 為0,malloc的行為是標(biāo)準(zhǔn)是未定義的,取決于編譯器。

當(dāng)然有分配就會有釋放,c語言也為我們提供了另一個函數(shù)free,專門用來做動態(tài)內(nèi)存的釋放以及回收,函數(shù)原型如下。

void free (void* ptr);

1.free函數(shù)用來釋放動態(tài)開辟的內(nèi)存。

2.如果參數(shù) ptr 指向的空間不是動態(tài)開辟的,那free函數(shù)的行為是未定義的。

3.如果參數(shù) ptr 是NULL指針,則函數(shù)什么事都不做。

我們看到下面的一個例子

#include <stdio.h>
int main()
{
	int* ptr = NULL;
	ptr = (int*)malloc(num * sizeof(int));
	if (NULL != ptr)//判斷ptr指針是否為空
	{
		int i = 0;
		for (i = 0; i < num; i++)
		{
			*(ptr + i) = 0;
		}
	}
	free(ptr);//釋放ptr所指向的動態(tài)內(nèi)存
	ptr = NULL;
	return 0;
}

相信這串代碼大家在有注釋的情況下都能夠看懂。但是有細(xì)心的讀者可能會發(fā)現(xiàn)free(ptr)之后又令ptr=NULL,這是為什么呢?其實在一塊內(nèi)存空間被釋放后,該指針仍指向被釋放掉的內(nèi)存地址,而此時的ptr便成了野指針,一旦后續(xù)不小心被程序調(diào)用就會導(dǎo)致程序崩潰,所以在指針釋放后要將其置為NULL防止這種情況的發(fā)生。

2.2 calloc函數(shù)

calloc函數(shù)類似與malloc,同樣用于動態(tài)內(nèi)存分配,函數(shù)原型如下。

void* calloc (size_t num, size_t size);

函數(shù)的功能是為 num個大小為size的元素開辟一塊空間,并且把空間的每個字節(jié)初始化為0。

與函數(shù)malloc的區(qū)別只在于calloc會在返回地址之前把申請的空間的每個字節(jié)初始化為全0。

2.3 realloc函數(shù)

有時會我們發(fā)現(xiàn)過去申請的空間太小了,有時候我們又會覺得申請的空間過大了,那為了合理的時 候內(nèi)存,我們一定會對內(nèi)存的大小做靈活的調(diào)整。那 realloc 函數(shù)就可以做到對動態(tài)開辟內(nèi)存大 小的調(diào)整。函數(shù)原型如下。

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

ptr 是要調(diào)整的內(nèi)存地址

size 調(diào)整之后新大小

返回值為調(diào)整之后的內(nèi)存起始位置。

這個函數(shù)調(diào)整原內(nèi)存空間大小的基礎(chǔ)上,還會將原來內(nèi)存中的數(shù)據(jù)移動到新的空間。

需要注意的是:

realloc在調(diào)整內(nèi)存空間時存在兩種情況

情況1:原有空間之后有足夠大的空間

情況2:原有空間之后沒有足夠大的空間

參見下圖示意

?情況1

當(dāng)是情況1的時候,要擴(kuò)展內(nèi)存就直接原有內(nèi)存之后直接追加空間,原來空間的數(shù)據(jù)不發(fā)生變化。

情況2

當(dāng)是情況2 的時候,原有空間之后沒有足夠多的空間時,擴(kuò)展的方法是:在堆空間上另找一個合適大小 的連續(xù)空間來使用。這樣函數(shù)返回的是一個新的內(nèi)存地址。

針對情況二,若realloc成功,指向原內(nèi)存地址的指針就成了懸掛指針,即指針指向了一塊沒有分配給用戶使用的內(nèi)存,如果再使用該指針進(jìn)行操作就可能發(fā)生意想不到的情況,因此要格外注意這種情況。

#include <stdio.h>
int main()
{
	int* ptr = (int*)malloc(100);//動態(tài)分配
	int* p = NULL;
	p = (int*)realloc(ptr, 1000);//重分配
	if (p != NULL)//判斷是否成功
	{
		ptr = p;//防止懸掛指針出現(xiàn)
	}
	//業(yè)務(wù)處理
	free(ptr);
    ptr=NULL;//釋放后置空防止野指針
	return 0;
}

注意動態(tài)內(nèi)存分配時應(yīng)當(dāng)像上述代碼一樣盡量規(guī)范。

3. 常見的動態(tài)內(nèi)存錯誤

3.1 對NULL指針進(jìn)行解引用操作

這個點就不再過多敘述,大家記住即可,對空指針進(jìn)行解引用操作可能會引發(fā)各種奇怪的問題。

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

同數(shù)組類似,即使是動態(tài)開辟的空間也不能越界訪問。

3.3 對非動態(tài)開辟內(nèi)存使用free釋放

切記只有動態(tài)開辟的內(nèi)存才能使用free。

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

參見下面代碼

void test()
{
 int *p = (int *)malloc(100);
 p++;
 free(p);//p不再指向動態(tài)內(nèi)存的起始位置
}

雖然有些小伙伴可能想既然free函數(shù)是根據(jù)指針來釋放內(nèi)存的,那我能不能通過對指針進(jìn)行操作去部分釋放動態(tài)分配的內(nèi)存呢?然而夢想很美好,現(xiàn)實很骨感。如果強(qiáng)行這樣做的話只可能會造成·更多不可預(yù)估的結(jié)果。

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

這個錯誤應(yīng)該大家目前應(yīng)該不太常犯,但是一旦后面代碼量大了之后就很有可能忘記是否已經(jīng)釋放過內(nèi)存從而導(dǎo)致重復(fù)釋放而bug。

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

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

看到這個標(biāo)題再看這串代碼,大家應(yīng)該都很容易能夠知道上面的代碼忘記釋放內(nèi)存了從而導(dǎo)致內(nèi)存泄漏,但實際日常我們非常容易忘記開辟內(nèi)存后free。

忘記釋放不再使用的動態(tài)開辟的空間會造成內(nèi)存泄漏。

切記:

動態(tài)開辟的空間一定要釋放,并且正確釋放 。

總結(jié)

原文鏈接:https://blog.csdn.net/weixin_60778429/article/details/122698675

欄目分類
最近更新