精品国产色欧洲激情_中文字幕在线欧美日韩制服在线_欢迎观看网站影片国产在线观看伊_日本高清一本视频_ww亚洲无码免费在线观看_午夜片无码区观看_欧美性爱福利资源_丰满少妇肥唇翘臀ⅩXX_欧美日韩另类久久_国内揄拍国内精品对白86

編程代碼
新聞詳情

程序員需要了解的硬核知識之內(nèi)存

發(fā)布時間:2020-11-30 11:19:55 最后更新:2020-12-01 09:02:25 瀏覽次數(shù):2523

我們都知道,計算機(jī)是處理數(shù)據(jù)的設(shè)備,而數(shù)據(jù)的主要存儲位置就是磁盤和內(nèi)存,并且對于程序員來講,CPU 和內(nèi)存是我們必須了解的兩個物理結(jié)構(gòu),它是你通向高階程序員很重要的橋梁,那么本篇文章我們就來介紹一下基本的內(nèi)存知識。

什么是內(nèi)存

內(nèi)存(Memory)是計算機(jī)中最重要的部件之一,它是程序與CPU進(jìn)行溝通的橋梁。計算機(jī)中所有程序的運(yùn)行都是在內(nèi)存中進(jìn)行的,因此內(nèi)存對計算機(jī)的影響非常大,內(nèi)存又被稱為主存,其作用是存放 CPU 中的運(yùn)算數(shù)據(jù),以及與硬盤等外部存儲設(shè)備交換的數(shù)據(jù)。只要計算機(jī)在運(yùn)行中,CPU 就會把需要運(yùn)算的數(shù)據(jù)調(diào)到主存中進(jìn)行運(yùn)算,當(dāng)運(yùn)算完成后CPU再將結(jié)果傳送出來,主存的運(yùn)行也決定了計算機(jī)的穩(wěn)定運(yùn)行。

內(nèi)存的物理結(jié)構(gòu)

在了解一個事物之前,你首先得先需要見過它,你才會有印象,才會有想要了解的興趣,所以我們首先需要先看一下什么是內(nèi)存以及它的物理結(jié)構(gòu)是怎樣的。

程序員需要了解的硬核知識之內(nèi)存

內(nèi)存的內(nèi)部是由各種IC電路組成的,它的種類很龐大,但是其主要分為三種存儲器

  • 隨機(jī)存儲器(RAM): 內(nèi)存中最重要的一種,表示既可以從中讀取數(shù)據(jù),也可以寫入數(shù)據(jù)。當(dāng)機(jī)器關(guān)閉時,內(nèi)存中的信息會 丟失。
  • 只讀存儲器(ROM):ROM 一般只能用于數(shù)據(jù)的讀取,不能寫入數(shù)據(jù),但是當(dāng)機(jī)器停電時,這些數(shù)據(jù)不會丟失。
  • 高速緩存(Cache):Cache 也是我們經(jīng)常見到的,它分為一級緩存(L1 Cache)、二級緩存(L2 Cache)、三級緩存(L3 Cache)這些數(shù)據(jù),它位于內(nèi)存和 CPU 之間,是一個讀寫速度比內(nèi)存更快的存儲器。當(dāng) CPU 向內(nèi)存寫入數(shù)據(jù)時,這些數(shù)據(jù)也會被寫入高速緩存中。當(dāng) CPU 需要讀取數(shù)據(jù)時,會直接從高速緩存中直接讀取,當(dāng)然,如需要的數(shù)據(jù)在Cache中沒有,CPU會再去讀取內(nèi)存中的數(shù)據(jù)。

內(nèi)存 IC 是一個完整的結(jié)構(gòu),它內(nèi)部也有電源、地址信號、數(shù)據(jù)信號、控制信號和用于尋址的 IC 引腳來進(jìn)行數(shù)據(jù)的讀寫。下面是一個虛擬的 IC 引腳示意圖

程序員需要了解的硬核知識之內(nèi)存

圖中 VCC 和 GND 表示電源,A0 - A9 是地址信號的引腳,D0 - D7 表示的是數(shù)據(jù)信號、RD 和 WR 都是控制信號,我用不同的顏色進(jìn)行了區(qū)分,將電源連接到 VCC 和 GND 后,就可以對其他引腳傳遞 0 和 1 的信號,大多數(shù)情況下,+5V 表示1,0V 表示 0。

我們都知道內(nèi)存是用來存儲數(shù)據(jù),那么這個內(nèi)存 IC 中能存儲多少數(shù)據(jù)呢?D0 - D7 表示的是數(shù)據(jù)信號,也就是說,一次可以輸入輸出 8 bit = 1 byte 的數(shù)據(jù)。A0 - A9 是地址信號共十個,表示可以指定 00000 00000 - 11111 11111 共 2 的 10次方 = 1024個地址。每個地址都會存放 1 byte 的數(shù)據(jù),因此我們可以得出內(nèi)存 IC 的容量就是 1 KB。

如果我們使用的是 512 MB 的內(nèi)存,這就相當(dāng)于是 512000(512 * 1000) 個內(nèi)存 IC。當(dāng)然,一臺計算機(jī)不太可能有這么多個內(nèi)存 IC ,然而,通常情況下,一個內(nèi)存 IC 會有更多的引腳,也就能存儲更多數(shù)據(jù)。

內(nèi)存的讀寫過程

讓我們把關(guān)注點放在內(nèi)存 IC 對數(shù)據(jù)的讀寫過程上來吧!我們來看一個對內(nèi)存IC 進(jìn)行數(shù)據(jù)寫入和讀取的模型

來詳細(xì)描述一下這個過程,假設(shè)我們要向內(nèi)存 IC 中寫入 1byte 的數(shù)據(jù)的話,它的過程是這樣的:

程序員需要了解的硬核知識之內(nèi)存

  • 首先給 VCC 接通 +5V 的電源,給 GND 接通 0V 的電源,使用 A0 - A9 來指定數(shù)據(jù)的存儲場所,然后再把數(shù)據(jù)的值輸入給 D0 - D7 的數(shù)據(jù)信號,并把 WR(write)的值置為 1,執(zhí)行完這些操作后,即可以向內(nèi)存 IC 寫入數(shù)據(jù)
  • 讀出數(shù)據(jù)時,只需要通過 A0 - A9 的地址信號指定數(shù)據(jù)的存儲場所,然后再將 RD 的值置為 1 即可。
  • 圖中的 RD 和 WR 又被稱為控制信號。其中當(dāng)WR 和 RD 都為 0 時,無法進(jìn)行寫入和讀取操作。

內(nèi)存的現(xiàn)實模型

為了便于記憶,我們把內(nèi)存模型映射成為我們現(xiàn)實世界的模型,在現(xiàn)實世界中,內(nèi)存的模型很想我們生活的樓房。在這個樓房中,1層可以存儲一個字節(jié)的數(shù)據(jù),樓層號就是地址,下面是內(nèi)存和樓層整合的模型圖

程序員需要了解的硬核知識之內(nèi)存

我們知道,程序中的數(shù)據(jù)不僅只有數(shù)值,還有數(shù)據(jù)類型的概念,從內(nèi)存上來看,就是占用內(nèi)存大?。ㄕ加脴菍訑?shù))的意思。即使物理上強(qiáng)制以 1 個字節(jié)為單位來逐一讀寫數(shù)據(jù)的內(nèi)存,在程序中,通過指定其數(shù)據(jù)類型,也能實現(xiàn)以特定字節(jié)數(shù)為單位來進(jìn)行讀寫。

下面是一個以特定字節(jié)數(shù)為例來讀寫指令字節(jié)的程序的示例

// 定義變量
char a;
short b;
long c;

// 變量賦值
a = 123;
b = 123;
c = 123;

我們分別聲明了三個變量 a,b,c ,并給每個變量賦上了相同的 123,這三個變量表示內(nèi)存的特定區(qū)域。通過變量,即使不指定物理地址,也可以直接完成讀寫操作,操作系統(tǒng)會自動為變量分配內(nèi)存地址。

這三個變量分別表示 1 個字節(jié)長度的 char,2 個字節(jié)長度的 short,表示4 個字節(jié)的 long。因此,雖然數(shù)據(jù)都表示的是 123,但是其存儲時所占的內(nèi)存大小是不一樣的。如下所示

程序員需要了解的硬核知識之內(nèi)存

這里的 123 都沒有超過每個類型的最大長度,所以 short 和 long 類型為所占用的其他內(nèi)存空間分配的數(shù)值是0,這里我們采用的是低字節(jié)序列的方式存儲

低字節(jié)序列:將數(shù)據(jù)低位存儲在內(nèi)存低位地址。

高字節(jié)序列:將數(shù)據(jù)的高位存儲在內(nèi)存地位的方式稱為高字節(jié)序列。

內(nèi)存的使用

指針

指針是 C 語言非常重要的特征,指針也是一種變量,只不過它所表示的不是數(shù)據(jù)的值,而是內(nèi)存的地址。通過使用指針,可以對任意內(nèi)存地址的數(shù)據(jù)進(jìn)行讀寫。

在了解指針讀寫的過程前,我們先需要了解如何定義一個指針,和普通的變量不同,在定義指針時,我們通常會在變量名前加一個 * 號。例如我們可以用指針定義如下的變量

char *d; // char類型的指針 d 定義
short *e; // short類型的指針 e 定義
long *f; // long類型的指針 f 定義

我們以32位計算機(jī)為例,32位計算機(jī)的內(nèi)存地址是 4 字節(jié),在這種情況下,指針的長度也是 32 位。然而,變量 d e f 卻代表了不同的字節(jié)長度,這是為什么呢?

實際上,這些數(shù)據(jù)表示的是從內(nèi)存中一次讀取的字節(jié)數(shù),比如 d e f 的值都為 100,那么使用 char 類型時就能夠從內(nèi)存中讀寫 1 byte 的數(shù)據(jù),使用 short 類型就能夠從內(nèi)存讀寫 2 字節(jié)的數(shù)據(jù), 使用 long 就能夠讀寫 4 字節(jié)的數(shù)據(jù),下面是一個完整的類型字節(jié)表

程序員需要了解的硬核知識之內(nèi)存

我們可以用圖來描述一下這個讀寫過程

程序員需要了解的硬核知識之內(nèi)存

數(shù)組是內(nèi)存的實現(xiàn)

數(shù)組是指多個相同的數(shù)據(jù)類型在內(nèi)存中連續(xù)排列的一種形式。作為數(shù)組元素的各個數(shù)據(jù)會通過下標(biāo)編號來區(qū)分,這個編號也叫做索引,如此一來,就可以對指定索引的元素進(jìn)行讀寫操作。

首先先來認(rèn)識一下數(shù)組,我們還是用 char、short、long 三種元素來定義數(shù)組,數(shù)組的元素用[value] 擴(kuò)起來,里面的值代表的是數(shù)組的長度,就像下面的定義

char g[100];
short h[100];
long i[100];

數(shù)組定義的數(shù)據(jù)類型,也表示一次能夠讀寫的內(nèi)存大小,char 、short 、long 分別以 1 、2 、4 個字節(jié)為例進(jìn)行內(nèi)存的讀寫。

數(shù)組是內(nèi)存的實現(xiàn),數(shù)組和內(nèi)存的物理結(jié)構(gòu)完全一致,尤其是在讀寫1個字節(jié)的時候,當(dāng)字節(jié)數(shù)超過 1 時,只能通過逐個字節(jié)來讀取,下面是內(nèi)存的讀寫過程

程序員需要了解的硬核知識之內(nèi)存

數(shù)組是我們學(xué)習(xí)的第一個數(shù)據(jù)結(jié)構(gòu),我們都知道數(shù)組的檢索效率是比較快的,至于數(shù)組的檢索效率為什么這么快并不是我們這篇文章討論的重點。

棧和隊列

我們上面提到數(shù)組是內(nèi)存的一種實現(xiàn),使用數(shù)組能夠使編程更加高效,下面我們就來認(rèn)識一下其他數(shù)據(jù)結(jié)構(gòu),通過這些數(shù)據(jù)結(jié)構(gòu)也可以操作內(nèi)存的讀寫。

棧(stack)是一種很重要的數(shù)據(jù)結(jié)構(gòu),棧采用 LIFO(Last In First Out)即后入先出的方式對內(nèi)存進(jìn)行操作。它就像一個大的收納箱,你可以往里面放相同類型的東西,比如書,最先放進(jìn)收納箱的書在最下面,最后放進(jìn)收納箱的書在最上面,如果你想拿書的話, 必須從最上面開始取,否則是無法取出最下面的書籍的。

棧的數(shù)據(jù)結(jié)構(gòu)就是這樣,你把書籍壓入收納箱的操作叫做壓入(push),你把書籍從收納箱取出的操作叫做彈出(pop),它的模型圖大概是這樣

程序員需要了解的硬核知識之內(nèi)存

入棧相當(dāng)于是增加操作,出棧相當(dāng)于是刪除操作,只不過叫法不一樣。棧和內(nèi)存不同,它不需要指定元素的地址。它的大概使用如下

// 壓入數(shù)據(jù)
Push(123);
Push(456);
Push(789);

// 彈出數(shù)據(jù)
j = Pop();
k = Pop();
l = Pop();

在棧中,LIFO 方式表示棧的數(shù)組中所保存的最后面的數(shù)據(jù)(Last In)會被最先讀取出來(First On)。

程序員需要了解的硬核知識之內(nèi)存

隊列

隊列和棧很相似但又不同,相同之處在于隊列也不需要指定元素的地址,不同之處在于隊列是一種 先入先出(First In First Out) 的數(shù)據(jù)結(jié)構(gòu)。隊列在我們生活中的使用很像是我們?nèi)ゾ皡^(qū)排隊買票一樣,第一個排隊的人最先買到票,以此類推,俗話說: 先到先得。它的使用如下

// 往隊列中寫入數(shù)據(jù)
EnQueue(123);
EnQueue(456);
EnQueue(789);

// 從隊列中讀出數(shù)據(jù)
m = DeQueue();
n = DeQueue();
o = DeQueue();

向隊列中寫入數(shù)據(jù)稱為 EnQueue()入列,從隊列中讀出數(shù)據(jù)稱為DeQueue()。

程序員需要了解的硬核知識之內(nèi)存

與棧相對,F(xiàn)IFO 的方式表示隊列中最先所保存的數(shù)據(jù)會優(yōu)先被讀取出來。

程序員需要了解的硬核知識之內(nèi)存

隊列的實現(xiàn)一般有兩種:順序隊列 和 循環(huán)隊列,我們上面的事例使用的是順序隊列,那么下面我們看一下循環(huán)隊列的實現(xiàn)方式

環(huán)形緩沖區(qū)

循環(huán)隊列一般是以環(huán)狀緩沖區(qū)(ring buffer)的方式實現(xiàn)的,它是一種用于表示一個固定尺寸、頭尾相連的緩沖區(qū)的數(shù)據(jù)結(jié)構(gòu),適合緩存數(shù)據(jù)流。假如我們要用 6 個元素的數(shù)組來實現(xiàn)一個環(huán)形緩沖區(qū),這時可以從起始位置開始有序的存儲數(shù)據(jù),然后再按照存儲時的順序把數(shù)據(jù)讀出。在數(shù)組的末尾寫入數(shù)據(jù)后,后一個數(shù)據(jù)就會從緩沖區(qū)的頭開始寫。這樣,數(shù)組的末尾和開頭就連接了起來。

程序員需要了解的硬核知識之內(nèi)存

鏈表

下面我們來介紹一下鏈表和 二叉樹,它們都是可以不用考慮索引的順序就可以對元素進(jìn)行讀寫的方式。通過使用鏈表,可以高效的對數(shù)據(jù)元素進(jìn)行添加 和 刪除操作。而通過使用二叉樹,則可以更高效的對數(shù)據(jù)進(jìn)行檢索。

在實現(xiàn)數(shù)組的基礎(chǔ)上,除了數(shù)據(jù)的值之外,通過為其附帶上下一個元素的索引,即可實現(xiàn)鏈表。數(shù)據(jù)的值和下一個元素的地址(索引)就構(gòu)成了一個鏈表元素,如下所示

程序員需要了解的硬核知識之內(nèi)存

對鏈表的添加和刪除都是非常高效的,我們來敘述一下這個添加和刪除的過程,假如我們要刪除地址為 p[2] 的元素,鏈表該如何變化呢?

程序員需要了解的硬核知識之內(nèi)存

我們可以看到,刪除地址為 p[2] 的元素后,直接將鏈表剔除,并把 p[2] 前一個位置的元素 p[1] 的指針域指向 p[2] 下一個鏈表元素的數(shù)據(jù)區(qū)即可。

程序員需要了解的硬核知識之內(nèi)存

那么對于新添加進(jìn)來的鏈表,需要確定插入位置,比如要在 p[2] 和 p[3] 之間插入地址為 p[6] 的元素,需要將 p[6] 的前一個位置 p[2] 的指針域改為 p[6] 的地址,然后將 p[6] 的指針域改為 p[3] 的地址即可。

鏈表的添加不涉及到數(shù)據(jù)的移動,所以鏈表的添加和刪除很快,而數(shù)組的添加設(shè)計到數(shù)據(jù)的移動,所以比較慢,通常情況下,使用數(shù)組來檢索數(shù)據(jù),使用鏈表來進(jìn)行添加和刪除操作。

二叉樹

二叉樹也是一種檢索效率非常高的數(shù)據(jù)結(jié)構(gòu),二叉樹是指在鏈表的基礎(chǔ)上往數(shù)組追加元素時,考慮到數(shù)組的大小關(guān)系,將其分成左右兩個方向的表現(xiàn)形式。假如我們把 50 這個值保存到了數(shù)組中,那么,如果接下來要進(jìn)行值寫入的話,就需要和50比較,確定誰大誰小,比50數(shù)值大的放右邊,小的放左邊,下圖是二叉樹的比較示例

程序員需要了解的硬核知識之內(nèi)存

二叉樹是由鏈表發(fā)展而來,因此二叉樹在追加和刪除元素方面也是同樣有效的。

這一切的演變都是以內(nèi)存為基礎(chǔ)的。

在線客服 雙翌客服
客服電話
  • 0755-23712116
  • 13822267203