少高潮爽了在观看奂费_奶水无码专区_欧美亚洲三级日韩_91精品国产综合香蕉_秋霞伦理电影在线_GOGO亚洲肉体艺术欣赏图片_一本一道a√无码中文字幕_免费看一级毛片无码区_内射视频网站在线观看_国产激情视频精品中文

編程代碼
新聞詳情

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

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

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

什么是內(nèi)存

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

內(nèi)存的使用

指針

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

在了解指針讀寫的過(guò)程前,我們先需要了解如何定義一個(gè)指針,和普通的變量不同,在定義指針時(shí),我們通常會(huì)在變量名前加一個(gè) * 號(hào)。例如我們可以用指針定義如下的變量

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

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

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

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

我們可以用圖來(lái)描述一下這個(gè)讀寫過(guò)程

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

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

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

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

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

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

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

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

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

棧和隊(duì)列

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

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

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

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

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

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

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

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

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

隊(duì)列

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

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

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

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

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

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

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

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

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

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

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

鏈表

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

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

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

對(duì)鏈表的添加和刪除都是非常高效的,我們來(lái)敘述一下這個(gè)添加和刪除的過(guò)程,假如我們要?jiǎng)h除地址為 p[2] 的元素,鏈表該如何變化呢?

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

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

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

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

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

二叉樹

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

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

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

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

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