C語言 動態內存管理全面解析

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("%s\n",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語言 動態內存管理內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: