首頁 > 軟體

C語言 動態記憶體管理全面解析

2022-02-11 13:02:27

1. 為什麼存在動態記憶體分配

*動態記憶體開闢在堆區*

我們已經掌握的開闢記憶體方式是型別直接定義變數,開闢的記憶體是固定的,像:

int a=20;  //在棧空間上開闢四個位元組

還有陣列,我們可以指定開闢空間的大小,像:

char arr[10] = {0};  ///在棧空間上開闢10個位元組的連續空間

但在程式執行時,很多時候我們會遇到記憶體不夠或者記憶體過多引起的浪費問題,那麼有沒有那種使用多少記憶體開闢多少記憶體的方法?這就是本篇文章要介紹的動態記憶體。

2. 動態記憶體函數的介紹

2.1 malloc和free

malloc和free都宣告在 stdlib.h 標頭檔案中

void* malloc (size_t size);
//向記憶體申請一塊連續可用的空間,並返回指向這塊空間的指標

如果開闢成功,則返回一個指向開闢好空間的指標。 如果開闢失敗,則返回一個NULL指標。 返回值的型別是 void* ,malloc函數並不知道開闢空間的型別,在使用的時候自己來決定。

void free (void* ptr);
//free函數用來釋放動態開闢的記憶體

如果引數 ptr 指向的空間不是動態開闢的,那free函數的行為是未定義的。

如果引數 ptr 是NULL指標,則函數什麼事都不做。

舉例:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
int main()
{
	//開闢10個整型空間
	int* p = (int*)malloc(40);
	if (NULL==p)
	{
		printf("%sn",strerror(errno));  //判斷開闢失敗的原因
		return 0;
	}
	//使用
 
	//釋放
	free(p);  //將空間還給系統,但是裡面的內容沒有改變,還可以通過p來找到地址
	p = NULL;  //因此要將地址置為空指標
	return 0;
}

2.2 calloc 

void* calloc (size_t num, size_t size);
//num為元素個數,size為每個元素的大小

注:與函數 malloc 的區別只在於 calloc 會在返回地址之前把申請的空間的每個位元組初始化為全0

當用calloc來開闢10個整型空間時

int* p = (int*)calloc(10,sizeof(int));

2.3 realloc

void* realloc (void* ptr, size_t size);
//ptr 是要調整的記憶體地址     size 調整之後新大小
//返回值為調整之後的記憶體起始位置

realloc在調整記憶體空間的是存在兩種情況:

1.當原地址後有足夠的空間時,可以接著原地址連續開闢空間,最後返回起始地址。

2.當原地址後空間不足以開闢我們所需要的空間時,那麼realloc會自動尋找一塊足以存放我們需要的的空間,並將原地址的內容複製到新空間中,釋放掉原地址中的內容,返回開闢出空間的初始地址。

我們可以先判斷是否開闢成功,再將地址賦予p

3. 常見的動態記憶體錯誤

3.1 對NULL指標的解除參照操作

開闢動態記憶體時,一定要注意對返回空指標的函數要進行判斷,防止對空指標進行解除參照,以免程式出現問題。

3.2 對動態開闢空間的越界存取

 int *p = (int *)malloc(10*sizeof(int));   //開闢記憶體
 
 if(NULL == p)    //判斷是否開闢成功
 {
 exit(EXIT_FAILURE);
 }
 
 int i=0;
 for(i=0; i<=10; i++)
 {
 *(p+i) = i;//當i是10的時候越界存取
 }
 free(p);

這塊可以像理解陣列一樣,不能存取下標為10的地址,會造成越界存取。

3.3 對非動態開闢記憶體使用free釋放

void test()
{
	int a = 10;
	int *p = &a;
	free(p);
}
 
 
int main()
{
	test();
	return 0;
}

不是動態記憶體開闢的空間記憶體不在堆區,沒必要用free釋放,在棧區開闢的空間在出了作用域後會自動還給系統,沒有必要,也不允許用free進行釋放。

3.4 使用free釋放一塊動態開闢記憶體的一部分

void test()
{
 int *p = (int *)malloc(100);
 p++;
 free(p);//p不再指向動態記憶體的起始位置
}

 不支援釋放一部分記憶體,這樣的寫法不支援不可取。只能從動態記憶體開闢的起始位置來進行釋放。

3.5 對同一塊動態記憶體多次釋放

void test()
{
 int *p = (int *)malloc(100);
 free(p);
 free(p);//重複釋放
}

重複釋放也會報錯

當p第一次釋放後,將p=NULL,再次釋放的話就不會有問題;寫程式碼是要避免重複釋放的情況,同時要記住每次釋放完之後都要將地址置為空指標。

若忘記釋放開闢的空間,就會造成記憶體漏失的問題(在釋放該段記憶體之前就失去了對該段記憶體的控制,從而造成了記憶體的浪費)

到此這篇關於C語言 動態記憶體管理深入理解的文章就介紹到這了,更多相關C語言 動態記憶體管理內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com