欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C語言動態(tài)內(nèi)存管理的實現(xiàn)示例

 更新時間:2024年11月26日 09:44:57   作者:米飯「」  
動態(tài)內(nèi)存管理是一種允許程序在運行時根據(jù)需要動態(tài)申請和回收內(nèi)存的策略,它提供了四種重要的函數(shù),本文就來介紹一下,感興趣的可以了解一下

一、什么是動態(tài)內(nèi)存管理

動態(tài)內(nèi)存管理是一種內(nèi)存管理方法。它允許程序在運行時根據(jù)需要動態(tài)地申請和回收內(nèi)存空間。與靜態(tài)內(nèi)存管理(如變量和數(shù)組的預(yù)先分配)不同,動態(tài)內(nèi)存管理根據(jù)程序的需求即時分配內(nèi)存,并且分配的內(nèi)存大小就是程序要求的大小。

動態(tài)內(nèi)存分配的優(yōu)點:

  • 能更好地適應(yīng)系統(tǒng)的動態(tài)需求,提高內(nèi)存的利用率
  • 可以根據(jù)程序的實際需要分配內(nèi)存,而不是預(yù)先分配固定大小的內(nèi)存
  • 允許程序在不需要時釋放內(nèi)存空間,供其他應(yīng)用程序使用

總結(jié)來說,動態(tài)內(nèi)存管理是一種靈活的內(nèi)存管理策略,它允許程序在運行時根據(jù)實際的需求分配和釋放內(nèi)存,從而提高內(nèi)存的使用效率和程序性能。

兩種開辟內(nèi)存空間的方式

我們現(xiàn)在已經(jīng)掌握的內(nèi)存開辟方式有兩種:一種是創(chuàng)建變量,還有一種就是創(chuàng)建數(shù)組。

int i = 5 //在??臻g上開辟4個字節(jié)char arr[10] //在??臻g上開辟10個字節(jié)的連續(xù)空間

但是上述的兩種開辟空間的方式有兩個特點:

  • 空間開辟的大小是固定的。
  • 數(shù)組在申明的時候,必須指定數(shù)組的長度,數(shù)組空間一旦確定了大小就不能再調(diào)整但是對于空間的需求,不僅僅是上述的情況。有時候我們需要的空間大小在程序運行的時候才能知道,那數(shù)組編譯時開辟空間的方式就不能滿足了。

在C語言引入了動態(tài)內(nèi)存開辟,讓程序員自己動態(tài)的申請和釋放空間,這樣就比較靈活了。

二、動態(tài)內(nèi)存管理的四個函數(shù)

要進行動態(tài)內(nèi)存分配,就要掌握四個重要的函數(shù):malloc、free、calloc、realloc。

1.malloc與free函數(shù)

C語言提供了一個動態(tài)內(nèi)存開辟的函數(shù):

void* malloc(size_t size);

這個函數(shù)會向內(nèi)存申請一塊連續(xù)可用的空間,并返回指向這塊空間的指針(地址)。
♥ 如果開辟成功,則返回一個指向開辟好空間的指針(這塊空間的起始地址)。
♥ 如果開辟失敗,則返回一個NULL指針,因此malloc的返回值一定要做檢查。
♥ 返回值的類型是void*,所以malloc函數(shù)并不知道開辟空間的類型,具體在使用的時候由使用者自己來決定。
♥ 如果參數(shù)size為0,那malloc的行為是標(biāo)準(zhǔn)未定義的,取決于編譯器。

上面malloc函數(shù)中的參數(shù)是size_t類型的,也就是一個無符號整型。而malloc的參數(shù)size就表示需要申請多少個字節(jié)的空間。我們都知道,void*類型的指針是無具體類型的指針,可以用來接收任意類型的地址,但是不能對其進行解引用。那malloc函數(shù)的返回值,我們要怎么接收呢?很簡單,對這個返回值進行強制類型轉(zhuǎn)換即可。因為我們在動態(tài)的申請空間時,心里就已經(jīng)知道自己需要多少個字節(jié)的空間,而且也知道申請的空間要用來存儲什么類型的數(shù)據(jù)。比如我們要申請20個字節(jié)的空間用來存放5個整型:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	//申請20個字節(jié)的空間用來存放5個整型
	int* p = (int*)malloc(20);
	//對指針p進行檢查,看是否為NULL
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//使用空間
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*(p + i) = i + 1;
	}
	//釋放空間
	free(p);
	p = NULL;
	return 0;
}

在申請空間時要考慮到,有申請失敗的時候,所以我們在動態(tài)的申請空間后,應(yīng)該要對malloc的返回值進行檢查。如果動態(tài)申請內(nèi)存失敗,那我們就不能再往下執(zhí)行程序。上面代碼中出現(xiàn)了另一個與malloc函數(shù)相匹配的free函數(shù),free函數(shù)是專門用來釋放動態(tài)申請的內(nèi)存空間的。free函數(shù)的定義如下:

void free(void* ptr)

free函數(shù)要傳進去的參數(shù)就是我們動態(tài)申請的內(nèi)存空間的起始地址,記住傳的參數(shù)一定得是這塊空間的起始地址。有借有還嘛!我們動態(tài)地向操作系統(tǒng)申請空間來使用,那使用完了就要把申請的內(nèi)存還回去。所以這里free函數(shù)就是??專門用來釋放和回收動態(tài)開辟的內(nèi)存空間的??。

但是對于free函數(shù)我們需要注意:
▲如果參數(shù)ptr指向的空間不是動態(tài)開辟的,那free函數(shù)的行為是未定義的
▲如果參數(shù)ptr是NULL指針,則函數(shù)什么事都不做
通過free函數(shù)釋放了動態(tài)開辟的空間以后,不是就完了!我們用來接收malloc函數(shù)的指針p,還指向著原來申請空間的起始地址,但是這塊空間已經(jīng)被釋放了。那以后要是不小又對指針p解引用來使用原來那塊空間,就會造成非法訪問,就會出現(xiàn)野指針。所以在釋放了動態(tài)開辟的內(nèi)存空間后,應(yīng)該將指針p置為空(NULL),這樣才是完整的。

malloc和free函數(shù)都聲明在stdlib.h的頭文件中,所以在使用這兩個函數(shù)之前,要記得包含頭文件stdlib.h。

由于動態(tài)開辟的內(nèi)存空間是連續(xù)的,那你把這塊空間看做數(shù)組也是可以的。因為數(shù)組在內(nèi)存中就是連續(xù)存放的。

2.calloc函數(shù)

C語言還提供了一個函數(shù)叫calloc,calloc函數(shù)也是用來動態(tài)內(nèi)存分配的。原型如下:

void* calloc(size_t num , size_t size)

♦ 函數(shù)的功能是為num個大小為size的元素開辟一塊空間,并且把空間的每個字節(jié)初始化為0
♦ 與函數(shù)malloc的區(qū)別只在于calloc函數(shù)會在返回地址之前把申請的空間的每個字節(jié)都初始化為0比如我們現(xiàn)要為5個大小為4個字節(jié)的整型元素申請一塊空間:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	//為5個大小為4字節(jié)的元素申請一塊空間
	int* p = (int*)calloc(5,sizeof(int));
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//打印空間內(nèi)容
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", *(p + i));
	}
	//釋放空間
	free(p);
	p = NULL;
	return 0;
}

程序運行結(jié)果:

在這里插入圖片描述

calloc函數(shù)除了在傳參形式上與malloc有一點區(qū)別外,再一個就是它會將所申請空間的每個字節(jié)全部初始化為0,再返回此空間的起始地址。在上面的運行結(jié)果中,我們看到確實將此空間的每個字節(jié)初始化為了全0。我們也可以在內(nèi)存中查看此空間的內(nèi)容,如下圖,確實初始化了空間內(nèi)容為全0。

在這里插入圖片描述

如果我們對申請的內(nèi)存空間的內(nèi)容要求初始化,那么可以很方便的使用calloc函數(shù)來完成任務(wù)。只是calloc函數(shù)在運行時間上可能就要比malloc函數(shù)慢,因為malloc函數(shù)不初始化申請的空間,所以兩個函數(shù)各有各的好處。同樣的,只要是動態(tài)開辟的內(nèi)存空間,都要判斷內(nèi)存空間是否開辟成功!都要用free函數(shù)釋放空間!都要把指針p置為空!養(yǎng)成良好的編程習(xí)慣,這有助于程序員少犯錯誤。

3.realloc函數(shù)

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

realloc函數(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::原有空間之后沒有足夠大的空間
◎ 情況3:如果調(diào)整失敗了,則realloc會返回NULL(空指針)

對于上面的第四第五條,如果我們一開始用malloc申請了20個字節(jié)的空間,現(xiàn)在要調(diào)整為40個字節(jié)的空間,那就有上面的三種情況,看下面的代碼:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	//動態(tài)開辟20個字節(jié)的空間
	int* ptr = (int*)malloc(5*sizeof(int));
	if (ptr == NULL)
	{
		perror("malloc");
		return 1;
	}
	//使用空間
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*(ptr + i) = i + 1;
	}
	//調(diào)整原來空間的大小為40個字節(jié)
	int* p = (int*)realloc(ptr, 10 * sizeof(int));
	if (p != NULL)//調(diào)整成功
	{
		ptr = p;
		int i = 0;
		for (i = 5; i < 10; i++)//繼續(xù)使用空間
		{
			*(ptr + i) = i + 1;
		}
		for (i = 0; i < 10; i++)//打印數(shù)據(jù)
		{
			printf("%d ", *(p + i));
		}
		//釋放內(nèi)存
		free(ptr);
		ptr = NULL;
	}
	else//調(diào)整失敗
	{
		perror("realloc");
		//釋放內(nèi)存
		free(ptr);
		ptr = NULL;
	}
	return 0;
}

程序運行結(jié)果:

在這里插入圖片描述

在這里插入圖片描述

如果使用realloc調(diào)整原來動態(tài)開辟的空間大小,那就有兩種情況:

  • 一種是上面的情況1,如果原來申請的空間后面有足夠大的尚未使用的空間,那realloc就在原來申請空間的后面繼續(xù)開辟20個字節(jié)空間,realloc返回的還是原來空間的起始地址。
  • 另一種是上面的情況2,如果原來開辟空間后面沒有足夠的未使用的空間,那realloc函數(shù)就會在堆區(qū)上重新找一塊連續(xù)的空間,這塊空間是滿足新的大小要求的,然后會將原來空間里的數(shù)據(jù)拷貝一份到新的空間里,然后再釋放原來那塊舊的空間,返回新的空間的起始地址。
  • 最后一種就是情況3,realloc函數(shù)如果開辟空間失敗了,那realloc函數(shù)會返回空指針。在上面的情況2中我們知道,如果realloc函數(shù)在堆區(qū)上重新找一塊空間來滿足現(xiàn)在的大小要求,那realloc函數(shù)會釋放原來的空間,返回新空間的地址。那我們可不可以直接將指針p賦值給ptr,讓指針ptr來維護這塊新空間呢?答案是不可以的。我們要考慮到realloc函數(shù)會有申請空間失敗的時候,要是realloc申請空間失敗,我們直接將p賦值給ptr的話,那原來開辟的空間也就找不到了。所以在將指針p賦值給ptr時,應(yīng)該要提前判斷指針p是不是空指針,不是,再將指針p賦值給ptr。這樣才是正確的。

在這里插入圖片描述

記住只要是動態(tài)開辟的空間,都要用free函數(shù)釋放申請的空間。

realloc還有一個特殊的用法,就是它可以完成和malloc一樣的功能,比如我們要用malloc申請20個字節(jié)的空間,那用realloc也可以實現(xiàn):

realloc(NULL , 20)

用realloc來開辟空間,只要記得傳的第一個參數(shù)是NULL(空指針),第二個參數(shù)就是我們要開辟的空間大小(單位字節(jié)),那現(xiàn)在的realloc就等價于malloc。

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

Ⅰ.對NULL指針的解引用操作

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(5 * sizeof(int));
	*p = 10;
	free(p);
	p = NULL;
	return 0;
}

這里就沒有對動態(tài)開辟的空間的指針(起始地址)進行檢查,如果malloc申請空間失敗,那malloc的返回值就是空指針。對NULL指針是不能對其進行解引用的。所以要對malloc的返回值做檢查:

在這里插入圖片描述

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

在這里插入圖片描述

對于動態(tài)申請的空間也是連續(xù)的,就像數(shù)組一樣。上面為10個整型動態(tài)的申請了40個字節(jié)的空間。但是在使用空間的時候,上面存在了越界訪問,我們申請來多少內(nèi)存空間,那我們就只有這塊空間的使用權(quán)限,不能越界使用空間。

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

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

我們說過只有動態(tài)開辟的空間,我們才會用free函數(shù)來釋放,對于其他非動態(tài)開辟的內(nèi)存空間,是不能用free來釋放的,這是未定義的。

Ⅳ.free只釋放了動態(tài)開辟內(nèi)存的一部分

在這里插入圖片描述

當(dāng)我們用一個指針接收了動態(tài)開辟的內(nèi)存空間的起始地址時,則對指針使用自加操作符,會改變它的指向位置,不再指向動態(tài)申請空間的起始位置,這時再將這個指針傳給free函數(shù),那free釋放的就只是這塊空間的一部分,并沒有完全回收內(nèi)存。所以對于動態(tài)開辟空間的指針(起始地址),要注意不要對其有任何的改動。

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

在這里插入圖片描述

我們在寫代碼時,對于動態(tài)開辟的內(nèi)存,我們可能會重復(fù)的釋放,這也是不行的。像這種錯誤,可能在我們寫大量的代碼時會遇到,也就是你知道前面你動態(tài)開辟內(nèi)存了,但是由于寫了很多行的代碼,可能就會忘記自己已經(jīng)對動態(tài)開辟的內(nèi)存釋放過了,你又來一次釋放。這也是動態(tài)開辟內(nèi)存會遇到的錯誤。所以一定要檢查用了多少次動態(tài)內(nèi)存開辟,對應(yīng)的就要有多少次回收釋放。

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

在這里插入圖片描述

忘記釋放不再使用的動態(tài)開辟的空間會造成內(nèi)存泄漏,你是向操作系統(tǒng)申請空間來用,但是你用了卻沒有把這塊空間還給操作系統(tǒng),那這塊內(nèi)存就可能還存儲著一些值,這塊空間就用不了,一直占著內(nèi)存,造成內(nèi)存泄漏。當(dāng)然,如果你忘記了使用free函數(shù)釋放動態(tài)開辟的空間,在程序運行結(jié)束的時候,操作系統(tǒng)也會自動回收這塊內(nèi)存。所以本質(zhì)上動態(tài)開辟的空間是不會丟的,但是如果程序一直運行著,那就可能會造成內(nèi)存泄漏。

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

到此這篇關(guān)于C語言動態(tài)內(nèi)存管理的實現(xiàn)示例的文章就介紹到這了,更多相關(guān)C語言動態(tài)內(nèi)存管理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺析C++宏定義里#和##的使用

    淺析C++宏定義里#和##的使用

    工作中如果是c開發(fā)的話,經(jīng)常會用到宏定義,而宏定義中的#和##也會時不時遇到,本文主要就來和大家分享一下這兩個符號的作用,需要的可以參考一下
    2023-05-05
  • C++之類和對象課后習(xí)題簡單實例

    C++之類和對象課后習(xí)題簡單實例

    下面小編就為大家?guī)硪黄狢++之類和對象課后習(xí)題簡單實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-07-07
  • C語言數(shù)據(jù)結(jié)構(gòu)圖的創(chuàng)建與遍歷實驗示例

    C語言數(shù)據(jù)結(jié)構(gòu)圖的創(chuàng)建與遍歷實驗示例

    這篇文章主要為大家介紹了C語言數(shù)據(jù)結(jié)構(gòu)圖的創(chuàng)建與遍歷實驗示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • C語言 位段的詳細介紹

    C語言 位段的詳細介紹

    這篇文章主要介紹了C語言 位段的詳細介紹的相關(guān)資料,學(xué)習(xí)C語言基礎(chǔ)的朋友,可以參考本文,需要的朋友可以參考下
    2016-11-11
  • OpenCV實現(xiàn)拼接圖像的簡單方法

    OpenCV實現(xiàn)拼接圖像的簡單方法

    這篇文章主要為大家詳細介紹了OpenCV實現(xiàn)拼接圖像的簡單方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-05-05
  • C++印刷模板使用方法詳解

    C++印刷模板使用方法詳解

    模板是C++支持參數(shù)化多態(tài)的工具,使用模板可以使用戶為類或者函數(shù)聲明一種一般模式,使得類中的某些數(shù)據(jù)成員或者成員函數(shù)的參數(shù)、返回值取得任意類型
    2022-11-11
  • 解析C++編程中如何使用設(shè)計模式中的狀態(tài)模式結(jié)構(gòu)

    解析C++編程中如何使用設(shè)計模式中的狀態(tài)模式結(jié)構(gòu)

    這篇文章主要介紹了如何在C++編程中適用設(shè)計模式中的狀態(tài)模式結(jié)構(gòu),狀態(tài)模式強調(diào)將特定狀態(tài)相關(guān)的邏輯分散到一些類的狀態(tài)類中,需要的朋友可以參考下
    2016-03-03
  • 詳解C++-二階構(gòu)造模式、友元

    詳解C++-二階構(gòu)造模式、友元

    這篇文章主要介紹了C++-二階構(gòu)造模式、友元,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • 深入分析C++中兩個大數(shù)相乘結(jié)果不正確的問題

    深入分析C++中兩個大數(shù)相乘結(jié)果不正確的問題

    本篇文章是對C++中兩個大數(shù)相乘結(jié)果不正確的問題進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • Qt讀寫ini文件之QSettings用法

    Qt讀寫ini文件之QSettings用法

    這篇文章主要為大家介紹了Qt讀寫ini文件之QSettings的使用方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-05-05

最新評論