詳解C++的靜態(tài)內(nèi)存分配與動(dòng)態(tài)內(nèi)存分配
I - 內(nèi)存分配概述
1.1 - 定義概述
內(nèi)存分配 (Memory Allocation) 是指為計(jì)算機(jī)程序或服務(wù)分配物理內(nèi)存空間或虛擬內(nèi)存空間的一個(gè)過(guò)程。通常在程序執(zhí)行前或執(zhí)行時(shí)完成內(nèi)存分配。
1.2 - 分類概述
存在兩種類型的內(nèi)存分配:
- 編譯時(shí)內(nèi)存分配或靜態(tài)內(nèi)存分配 (Compile-time or Static Memory Allocation)
- 運(yùn)行時(shí)內(nèi)存分配或動(dòng)態(tài)內(nèi)存分配 (Run-time or Dynamic Memory Allocation)
靜態(tài)內(nèi)存分配:
靜態(tài)內(nèi)存分配是由編譯器為聲明的變量分配內(nèi)存。內(nèi)存的地址可以通過(guò)地址操作符找到,并且可以賦值給指針變量。該內(nèi)存是在編譯時(shí)分配的。
動(dòng)態(tài)內(nèi)存分配 :
在程序執(zhí)行時(shí) (execution) 或 運(yùn)行時(shí) (run-time) 進(jìn)行的內(nèi)存分配被稱為動(dòng)態(tài)內(nèi)存分配。庫(kù)函數(shù)例如 calloc() 和 malloc() 或者操作符 new 均支持分配動(dòng)態(tài)內(nèi)存。動(dòng)態(tài)分配的內(nèi)存空間,通過(guò)這些函數(shù)或操作符的返回值分配,賦值給指針變量。
1.3 - 區(qū)別概述
序號(hào) | 靜態(tài)內(nèi)存分配 | 動(dòng)態(tài)內(nèi)存分配 |
---|---|---|
1 | 在靜態(tài)內(nèi)存分配中,變量被永久地分配內(nèi)存,直到程序執(zhí)行結(jié)束/函數(shù)調(diào)用結(jié)束 | 在動(dòng)態(tài)內(nèi)存分配中,只有當(dāng)你的程序單元被激活時(shí)才會(huì)為變量分配內(nèi)存 |
2 | 靜態(tài)內(nèi)存分配在程序執(zhí)行前完成 | 動(dòng)態(tài)內(nèi)存分配在程序執(zhí)行過(guò)程中完成 |
3 | 使用棧來(lái)管理靜態(tài)分配的內(nèi)存 | 使用堆來(lái)管理動(dòng)態(tài)分配的內(nèi)存 |
4 | 較不高效 (less efficient) | 較高效 |
5 | 在靜態(tài)內(nèi)存分配中,不存在內(nèi)存的重用 | 在動(dòng)態(tài)內(nèi)存分配中,存在內(nèi)存的重用,而且在不需要時(shí)可以內(nèi)存可以被釋放 |
6 | 在靜態(tài)內(nèi)存分配中,一旦內(nèi)存被分配,內(nèi)存大小就不能再改變 | 在動(dòng)態(tài)內(nèi)存分配中,分配了內(nèi)存后,內(nèi)存的大小可以改變 |
7 | 在靜態(tài)內(nèi)存分配方案中,我們不能重新使用未使用的內(nèi)存 | 動(dòng)態(tài)內(nèi)存分配中,允許重復(fù)使用內(nèi)存。用戶可以在需要時(shí)分配更多的內(nèi)存。同時(shí),用戶也可以在需要時(shí)釋放內(nèi)存。 |
8 | 在這種內(nèi)存分配方案中,執(zhí)行速度比動(dòng)態(tài)內(nèi)存分配要快 | 在這種內(nèi)存分配方案中,執(zhí)行速度要比靜態(tài)內(nèi)存分配慢 |
9 | 編譯時(shí)內(nèi)存分配 | 運(yùn)行時(shí)內(nèi)存分配 |
10 | 靜態(tài)分配的內(nèi)存從程序開(kāi)始保持到程序結(jié)束 | 動(dòng)態(tài)分配的內(nèi)存可以在任意時(shí)刻釋放 |
11 | 靜態(tài)的內(nèi)存分配常常用于數(shù)組等 | 動(dòng)態(tài)的內(nèi)存分配常常用于鏈表等數(shù)據(jù)結(jié)構(gòu) |
II - 靜態(tài)內(nèi)存分配
內(nèi)存必須被分配給我們所創(chuàng)建的變量,這樣實(shí)際的變量才能存在?,F(xiàn)在有一個(gè)問(wèn)題,即我們認(rèn)為它是如何運(yùn)行的,以及它實(shí)際上是如何運(yùn)行的?
計(jì)算機(jī)如何創(chuàng)建一個(gè)變量?
當(dāng)我們思考如何創(chuàng)造某樣?xùn)|西時(shí),我們會(huì)想到“從零開(kāi)始”著手干,而當(dāng)計(jì)算機(jī)創(chuàng)建一個(gè)變量 ‘X’ 時(shí),實(shí)際上情況并不是這樣;對(duì)于計(jì)算機(jī)而言,更像是一種分配,計(jì)算機(jī)只是從許多預(yù)先存在的內(nèi)存單元中分配一個(gè)內(nèi)存單元給 X。
那么什么是靜態(tài)內(nèi)存分配?當(dāng)我們聲明變量時(shí),我們實(shí)際上是在準(zhǔn)備所有會(huì)被使用的變量,這樣編譯器就知道被使用的變量實(shí)際上是用戶想要的程序的重要部分,而不是到處漂浮的流氓符號(hào)。因此,當(dāng)我們聲明變量時(shí),編譯器實(shí)際做的是將這些變量分配到它們的房間(一個(gè)內(nèi)存單元)?,F(xiàn)在,可以看出,這是在程序執(zhí)行前完成的,你不能在程序執(zhí)行時(shí)用這種方法分配變量。
示例
void func() { int a; } int main() { int b; int c[12]; //... }
上述代碼中所有的變量都是靜態(tài)分配的。
III - 動(dòng)態(tài)內(nèi)存分配
那么,既然已經(jīng)存在一種方式來(lái)完成內(nèi)存分配的工作,為什么我們需要引入另一種分配方法?為什么我們需要在程序執(zhí)行過(guò)程中分配內(nèi)存?
因?yàn)?,盡管不是很顯而易見(jiàn),但不能在運(yùn)行時(shí)分配內(nèi)存,就降低了靈活性,并與空間效率相妥協(xié)。特別是,在那些事先不知道輸入的情況下,我們會(huì)在存儲(chǔ)的低效使用和缺乏或過(guò)多的空間用來(lái)輸入數(shù)據(jù)方面受到影響(給定一個(gè)固定長(zhǎng)度的數(shù)組或類似的數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ)數(shù)據(jù))。
所以引入動(dòng)態(tài)內(nèi)存分配: 在運(yùn)行時(shí),存儲(chǔ)/內(nèi)存/單元可以分配給變量的機(jī)制被稱為動(dòng)態(tài)內(nèi)存分配(不要與 DMA 相混淆)。因此,我們可以知道在運(yùn)行期間分配內(nèi)存,這使我們能夠使用我們想要的存儲(chǔ),而不用擔(dān)心任何浪費(fèi)或者不足。
為什么要使用動(dòng)態(tài)分配的原因
- 當(dāng)我們事先不知道程序需要多少內(nèi)存的時(shí);
- 當(dāng)我們希望數(shù)據(jù)結(jié)構(gòu)沒(méi)有固定的內(nèi)存空間上限時(shí);
- 當(dāng)你想更有效地使用你的內(nèi)存空間時(shí)。例如: 如果你為一個(gè)一維數(shù)組分配的內(nèi)存空間是
array[20]
,而你最終只使用了 10 個(gè)內(nèi)存空間,那么剩下的 10 個(gè)內(nèi)存空間就被浪費(fèi)了,這些浪費(fèi)的內(nèi)存甚至不能被其他程序變量所使用; - 動(dòng)態(tài)創(chuàng)建的列表的插入和刪除可以非常容易地通過(guò)操作地址來(lái)完成,而在靜態(tài)分配的內(nèi)存中,插入和刪除會(huì)導(dǎo)致更多的移動(dòng)和內(nèi)存浪費(fèi);
- 當(dāng)你想在編程中使用結(jié)構(gòu)和鏈表的概念時(shí),動(dòng)態(tài)內(nèi)存分配是必須的
C++ 代碼
int main(int argc, char* argv[]) { // 動(dòng)態(tài)內(nèi)存分配 int* ptr = new int; int* ptr1 = new int[10]; // 動(dòng)態(tài)分配內(nèi)存的釋放 delete ptr; delete [] ptr1; }
C 代碼
ptr = calloc(m, n);
等價(jià)于
ptr = malloc(m * n); memset(ptr, 0, m * n);
IV - 小結(jié)
有兩種類型的可用內(nèi)存 – 棧 (stack) 和堆 (heap)。靜態(tài)內(nèi)存分配只能在棧上進(jìn)行,而動(dòng)態(tài)內(nèi)存分配可以在棧和堆上進(jìn)行。在堆上進(jìn)行動(dòng)態(tài)分配的一個(gè)例子是遞歸,在遞歸中,函數(shù)按照出現(xiàn)的順序被放入調(diào)用堆,并在到達(dá)基數(shù)時(shí)一個(gè)一個(gè)地彈出。
當(dāng)在堆上分配內(nèi)存時(shí),我們需要手動(dòng)刪除內(nèi)存,因?yàn)榧词狗峙涞膬?nèi)存范圍結(jié)束(如棧的情況),內(nèi)存也不會(huì)被編譯器自己釋放(取消分配 deallocate)。
4.1 - 靜態(tài)分配的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
- 使用簡(jiǎn)單
- 分配和取消分配都由編譯器完成
- 高效的執(zhí)行時(shí)間
- 它使用棧數(shù)據(jù)結(jié)構(gòu)
缺點(diǎn)
- 內(nèi)存浪費(fèi)問(wèn)題
- 必須知道確切的內(nèi)存需求
- 一旦初始化后,內(nèi)存的大小不能調(diào)整
4.2 - 動(dòng)態(tài)分配的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
- 動(dòng)態(tài)分配是在運(yùn)行時(shí)進(jìn)行的
- 只要我們需要,我們就可以分配(創(chuàng)建)額外的存儲(chǔ)
- 只要我們使用結(jié)束了,內(nèi)存就可以被取消分配(free / delete)動(dòng)態(tài)空間
- 因此,人們總是可以準(zhǔn)確地?fù)碛兴璧目臻g量–不多也不少。
- 如果需要,內(nèi)存大小可以重新分配
缺點(diǎn)
- 由于內(nèi)存是在運(yùn)行時(shí)分配的,因此需要更多的時(shí)間。
- 當(dāng)完成后,內(nèi)存需要由用戶釋放。這一點(diǎn)很重要,因?yàn)樗锌赡茏兂呻y以發(fā)現(xiàn)的 bug。
總上所述,靜態(tài)內(nèi)存是編譯器提前分配的東西。而動(dòng)態(tài)內(nèi)存是在執(zhí)行過(guò)程中由程序控制的東西。程序可以要求更多的內(nèi)存,也可以刪除部分分配的內(nèi)存。
到此這篇關(guān)于詳解C++的靜態(tài)內(nèi)存分配與動(dòng)態(tài)內(nèi)存分配的文章就介紹到這了,更多相關(guān)C++ 內(nèi)存分配內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- c++ 動(dòng)態(tài)內(nèi)存分配相關(guān)總結(jié)
- C++使用動(dòng)態(tài)內(nèi)存分配的原因解說(shuō)
- 帶你了解C++的動(dòng)態(tài)內(nèi)存分配
- C語(yǔ)言編程C++動(dòng)態(tài)內(nèi)存分配示例講解
- c/c++內(nèi)存分配大小實(shí)例講解
- C++ 操作系統(tǒng)內(nèi)存分配算法的實(shí)現(xiàn)詳解
- C++繼承和動(dòng)態(tài)內(nèi)存分配
- 淺析C++中的動(dòng)態(tài)內(nèi)存分配
- C/C++的堆棧內(nèi)存分配的實(shí)現(xiàn)
相關(guān)文章
關(guān)于C++面向?qū)ο笤O(shè)計(jì)的訪問(wèn)性問(wèn)題詳解
這篇文章主要給大家介紹了關(guān)于C++面向?qū)ο笤O(shè)計(jì)的訪問(wèn)性問(wèn)題的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-09-09如何使用C語(yǔ)言實(shí)現(xiàn)細(xì)菌的繁殖與擴(kuò)散
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)細(xì)菌的繁殖與擴(kuò)散,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11C++實(shí)現(xiàn)LeetCode(2.兩個(gè)數(shù)字相加)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(兩個(gè)數(shù)字相加),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07C語(yǔ)言靜態(tài)鏈表和動(dòng)態(tài)鏈表
靜態(tài)鏈表和動(dòng)態(tài)鏈表是線性表鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)的兩種不同的表示方式。靜態(tài)鏈表的初始長(zhǎng)度一般是固定的,在做插入和刪除操作時(shí)不需要移動(dòng)元素,僅需修改指針。動(dòng)態(tài)鏈表是相對(duì)于靜態(tài)鏈表而言的,一般地,在描述線性表的鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)時(shí)如果沒(méi)有特別說(shuō)明即默認(rèn)描述的是動(dòng)態(tài)鏈表。2016-05-05C語(yǔ)言銀行系統(tǒng)課程設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言銀行系統(tǒng)課程設(shè)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01C++判斷主機(jī)是否處于聯(lián)網(wǎng)狀態(tài)
這篇文章主要為大家詳細(xì)介紹了C++判斷主機(jī)是否處于聯(lián)網(wǎng)狀態(tài),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06