C語(yǔ)言動(dòng)態(tài)內(nèi)存的分配實(shí)例詳解
前言
給數(shù)組分配多大的空間?
你是否和初學(xué)C時(shí)的我一樣,有過(guò)這樣的疑問。
這一期博客就來(lái)聊一聊動(dòng)態(tài)內(nèi)存的分配
讀完這篇文章,你可能對(duì)內(nèi)存的分配有一個(gè)更好的理解
??動(dòng)態(tài)內(nèi)存分配的定義
首先我們要搞清楚什么是動(dòng)態(tài)內(nèi)存的分配
平常我們定義的數(shù)組,都是在棧區(qū)分配的空間,都是分配的空間都是固定的大小
這種分配固定大小的內(nèi)存分配方法稱之為靜態(tài)內(nèi)存分配
與靜態(tài)內(nèi)存相對(duì)的,就是可以控制內(nèi)存的分配的動(dòng)態(tài)內(nèi)存分配
??注意:這里動(dòng)態(tài)內(nèi)存分配的空間是在堆區(qū)申請(qǐng)的,不是在棧區(qū)申請(qǐng)的
??這里要講一下什么是棧區(qū),什么是堆區(qū)
內(nèi)存的空間并不是都是一樣的,在學(xué)習(xí)C語(yǔ)言時(shí),提到的區(qū)域大致上分為棧區(qū),堆區(qū),和靜態(tài)區(qū)。就比如說(shuō)在一個(gè)車間一樣,不同的區(qū)域做著不同的事,就有不同的功能,但是這些不同的功能又不是毫不相關(guān)的,他們彼此聯(lián)系,相互構(gòu)成整個(gè)內(nèi)存空間.

??動(dòng)態(tài)內(nèi)存的優(yōu)勢(shì)
<1> 可以控制內(nèi)存的大小
在很多時(shí)候,我們申請(qǐng)的空間是未知的
就比如說(shuō)通訊錄,在剛剛開始用的時(shí)候很小的空間就足夠了,但是在未來(lái)你不知道你需要存下多少個(gè)號(hào)碼,這時(shí)候就存在一個(gè)問題,你定的空間需要多少個(gè)字節(jié),當(dāng)申請(qǐng)的太少,就會(huì)出現(xiàn)存不下去的情況,如果存的空間過(guò)大,有會(huì)造成一定的浪費(fèi)。
在動(dòng)態(tài)內(nèi)存分配就可以避免這個(gè)問題,你可以運(yùn)用 reallac 控制大小,當(dāng)內(nèi)存達(dá)到申請(qǐng)的空間時(shí),就會(huì)主動(dòng)擴(kuò)容,也就是再次向內(nèi)存申請(qǐng)空間。
<2> 可以多次利用這部分空間
靜態(tài)內(nèi)存分配利用的空間,整個(gè)程序結(jié)束才會(huì)釋放給系統(tǒng)
而動(dòng)態(tài)內(nèi)存分配的空間,只能在函數(shù)運(yùn)行結(jié)束后由系統(tǒng)自動(dòng)釋放,需要用戶主動(dòng)去釋放,可以通過(guò)利用完(就比如說(shuō)打印元素,打印完),用戶再通過(guò) free函數(shù)釋放 這塊申請(qǐng)的空間,當(dāng)再次用動(dòng)態(tài)內(nèi)存申請(qǐng)空間時(shí),就可以再次利用這塊空間,這樣也能在一定程度上,可以節(jié)省一定的空間。
<3>不占用棧區(qū)的內(nèi)存
假設(shè)棧區(qū)定義了變量
而每個(gè)變量分配內(nèi)存時(shí),之間又有一定的間隙
當(dāng)定義的變量足夠多時(shí),空隙也會(huì)很多
這時(shí)候向系統(tǒng)申請(qǐng)一個(gè)比較大且連續(xù)的空間時(shí),雖然有足夠的空間,但是缺少了連續(xù)的空間
就無(wú)法申請(qǐng)到這部分空間
所以動(dòng)態(tài)內(nèi)存在堆區(qū)申請(qǐng),就完全不必?fù)?dān)心棧區(qū)的空間不夠的問題
說(shuō)到這里,你是不是有一個(gè)疑惑,為什么空間的內(nèi)存存在棧區(qū)和堆區(qū)之分
如果感興趣,可以參考這個(gè)回答——為什么存在棧區(qū)堆區(qū)
??malloc calloc realloc和free函數(shù)的介紹
在動(dòng)態(tài)內(nèi)存的分配中,離不開malloc與calloc,這兩個(gè)函數(shù)都是向內(nèi)存申請(qǐng)空間
| calloc |
| 頭文件 #include <stdlib.h> |
| 格式 void *calloc(size_t num, size_t size); |
| 功能 為num個(gè)大小為size字節(jié)的對(duì)象分配存儲(chǔ)空間,該空間內(nèi)的所有位都會(huì)初始化為。 |
| 返回值 若分配成功,則返回一個(gè)指向已分配的空間開頭的指針;若分配失敗,則返回空指針 |
這兩個(gè)函數(shù)都是向系統(tǒng)申請(qǐng)動(dòng)態(tài)內(nèi)存空間,他們的頭文件,返回值和功能大致都是相同的
不同的是calloc函數(shù)開辟的空間,就會(huì)將空間的內(nèi)容全部初始話為零
而,malloc函數(shù)向系統(tǒng)申請(qǐng)的空間,空間的值都是隨機(jī)的
realloc |
| 頭文件 #include <stdlib.h> |
| 格式 void *realloc(void *mem_address, unsigned int newsize); |
| 功能 先判斷當(dāng)前的指針是否有足夠的連續(xù)空間,如果有,擴(kuò)大mem_address指向的地址,并且將 mem_address返回,如果空間不夠,先按照newsize指定的大小分配空間,將原有數(shù)據(jù)從頭到尾拷貝 到新分配的內(nèi)存區(qū)域,而后釋放原來(lái)mem_address所指內(nèi)存區(qū)域(注意:原來(lái)指針是自動(dòng)釋放,不 需要使用free),同時(shí)返回新分配的內(nèi)存區(qū)域的首地址。即重新分配存儲(chǔ)器塊的地址。 |
| 返回值 如果重新分配成功則返回指向被分配內(nèi)存的指針,否則返回空指針NULL。 |
??動(dòng)態(tài)空間的申請(qǐng)與釋放
講完動(dòng)態(tài)內(nèi)存申請(qǐng)的相關(guān)函數(shù),那具體的代碼實(shí)現(xiàn)是什么呢
<1> double *x;
<2> x=calloc(1,sizeof(double))或者x=malloc(sizeof(double));
<3> free;

下面動(dòng)態(tài)分配的內(nèi)存賦值并顯示
??為單個(gè)對(duì)象分配空間
#include<stdio.h>
#include<stdlib.h>
int main()//動(dòng)態(tài)內(nèi)存的賦值與顯示
{
int* a;
a = malloc(sizeof(int)); //分配動(dòng)態(tài)內(nèi)存
if (a == NULL) //是否成功分配了儲(chǔ)存空間,否則返回分配失敗
printf("分配失敗");
else
{
*a = 20;
printf("*a=%d\n", *a);
free(a); //釋放
}
return 0;
}?? 為數(shù)組分配空間
#include<stdio.h>
#include<stdlib.h>
int main()//動(dòng)態(tài)內(nèi)存的賦值與顯示
{
int n = 0; int* a; int i = 0;
printf("輸入分配空間元素的個(gè)數(shù):>");
scanf_s("%d", &n);
a =(int *) calloc(n,sizeof(int));
if (a == NULL)
printf("分配失敗");
else
{
for (i = 0; i < n; i++)
{
*(a + i) = i;
printf("*a=%d\n", *(a+i));
}
free(a);
}
return 0;
}這里其實(shí)沒有“為數(shù)組開辟的空間”這一說(shuō)
因?yàn)閯?dòng)態(tài)申請(qǐng)的空間都是一個(gè)一個(gè)的“塊”
不難發(fā)現(xiàn),calloc與malloc的差別并不大,只有第一個(gè)參數(shù)不同
在這兩行代碼中,存在著一個(gè)小細(xì)節(jié)
a = malloc(sizeof(int));
a =(int *) calloc(n,sizeof(int));
這兩者的差別不僅僅是函數(shù)的不同,其中后者有強(qiáng)制類型轉(zhuǎn)換,而前者沒有
實(shí)際上在C語(yǔ)言的標(biāo)準(zhǔn)上,有無(wú)強(qiáng)制類型轉(zhuǎn)換都是行得通的(當(dāng)然在c++必須將強(qiáng)制類型轉(zhuǎn)換)
因?yàn)闊o(wú)論是calloc還是malloc,他們的返回值都是void* ,這里的void*實(shí)際上可以轉(zhuǎn)換為int*類型或者其他類型,換句話說(shuō),就是返回的指針是兼容所有類型的萬(wàn)能指針。
??即指向void型的指針可以指向任意類型的對(duì)象,是一種特殊類型的指針。
指向void型的指針的值可以賦給指向任意類型的指針,反之亦可。
??改變申請(qǐng)的動(dòng)態(tài)內(nèi)存(realloc的使用)
#include<stdlib.h>
#include<stdio.h>
int main()
{
int* a; int i = 0;
a=(int *)calloc(10,sizeof(int));
if (a == NULL)
printf("分配失敗");
//使用
else
{
for (i = 0; i < 10; i++)
{
*(a + i) = i;
printf("*a=%d\n", *(a + i));
}
//需要擴(kuò)容
int* ret = realloc(a, 80);
if (ret != NULL)
{
a = ret;
}
free(a);
a=NULL;
}
}??擴(kuò)容不能直接就a=realloc(a, 80),需要中間引一個(gè)中間變量*ret
??擴(kuò)容可能有三種情況
情況一(在a的地址處,有空余的空間來(lái)擴(kuò)容)

情況二 (在a的地址處,沒有空余的空間來(lái)擴(kuò)容,但是有其他的空間可存儲(chǔ)擴(kuò)容后的空間)

情況三(reallo調(diào)整空間失?。?/p>

在這三種情況中,第一種的地址不變
第二種會(huì)在一個(gè)新的地方申請(qǐng)足夠大的地方,此時(shí)的地址不在是a原先的地址
第三種就擴(kuò)容失敗,就會(huì)導(dǎo)致擴(kuò)容前申請(qǐng)的空間,也發(fā)生了改變,所以不能直接用a來(lái)重新賦值,
總結(jié)
到此這篇關(guān)于C語(yǔ)言動(dòng)態(tài)內(nèi)存分配的文章就介紹到這了,更多相關(guān)C語(yǔ)言動(dòng)態(tài)內(nèi)存分配內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C語(yǔ)言動(dòng)態(tài)內(nèi)存分配和內(nèi)存操作函數(shù)使用詳解
- C語(yǔ)言動(dòng)態(tài)內(nèi)存的分配最全面分析
- 深入了解C語(yǔ)言中的動(dòng)態(tài)內(nèi)存分配
- C語(yǔ)言深入講解動(dòng)態(tài)內(nèi)存分配函數(shù)的使用
- c語(yǔ)言詳解動(dòng)態(tài)內(nèi)存分配及常見錯(cuò)誤的解決
- C語(yǔ)言深入探索動(dòng)態(tài)內(nèi)存分配的使用
- C語(yǔ)言的動(dòng)態(tài)內(nèi)存分配及動(dòng)態(tài)內(nèi)存分配函數(shù)詳解
- C語(yǔ)言動(dòng)態(tài)內(nèi)存分配圖文講解
相關(guān)文章
static全局變量與普通的全局變量的區(qū)別詳細(xì)解析
以下是對(duì)static全局變量與普通的全局變量的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-09-09
C語(yǔ)言 深入解讀數(shù)據(jù)結(jié)構(gòu)之堆的實(shí)現(xiàn)
堆就是用數(shù)組實(shí)現(xiàn)的二叉樹,所以它沒有使用父指針或者子指針。堆根據(jù)“堆屬性”來(lái)排序,“堆屬性”決定了樹中節(jié)點(diǎn)的位置2021-11-11
Visual Studio 2022中創(chuàng)建的C++項(xiàng)目無(wú)法使用萬(wàn)能頭<bits/stdc++.h>的
如果大家也遇到下面這種問題,可能是沒有include文件夾中沒有bits/stdc++.h,這篇文章主要介紹了Visual Studio 2022中創(chuàng)建的C++項(xiàng)目無(wú)法使用萬(wàn)能頭<bits/stdc++.h>的解決方案,感興趣的朋友跟隨小編一起看看吧2024-02-02
c++調(diào)用python實(shí)現(xiàn)圖片ocr識(shí)別
所謂c++調(diào)用python,實(shí)際上就是在c++中把整個(gè)python當(dāng)作一個(gè)第三方庫(kù)引入,然后使用特定的接口來(lái)調(diào)用python的函數(shù)或者直接執(zhí)行python腳本,本文介紹的是調(diào)用python實(shí)現(xiàn)圖片ocr識(shí)別,感興趣的可以了解下2023-09-09

