咱們都知道,計算機是處理數據的設備,而數據的主要存儲位置就是磁盤和內存,而且對於程序員來說,CPU 和內存是咱們必須瞭解的兩個物理結構,它是你通向高階程序員很重要的橋樑,那麼本篇文章咱們就來介紹一下基本的內存知識。程序員
咱們都知道,計算機是處理數據的設備,而數據的主要存儲位置就是磁盤和內存,而且對於程序員來說,CPU 和內存是咱們必須瞭解的兩個物理結構,它是你通向高階程序員很重要的橋樑,那麼本篇文章咱們就來介紹一下基本的內存知識。編程
內存(Memory)是計算機中最重要的部件之一,它是程序與CPU進行溝通的橋樑。計算機中全部程序的運行都是在內存中進行的,所以內存對計算機的影響很是大,內存又被稱爲主存,其做用是存放 CPU 中的運算數據,以及與硬盤等外部存儲設備交換的數據。只要計算機在運行中,CPU 就會把須要運算的數據調到主存中進行運算,當運算完成後CPU再將結果傳送出來,主存的運行也決定了計算機的穩定運行。數組
在瞭解一個事物以前,你首先得先須要見過它,你纔會有印象,纔會有想要了解的興趣,因此咱們首先須要先看一下什麼是內存以及它的物理結構是怎樣的。緩存
內存的內部是由各類IC電路組成的,它的種類很龐大,可是其主要分爲三種存儲器數據結構
隨機存儲器(RAM):內存中最重要的一種,表示既能夠從中讀取數據,也能夠寫入數據。當機器關閉時,內存中的信息會 丟失。學習
只讀存儲器(ROM):ROM 通常只能用於數據的讀取,不能寫入數據,可是當機器停電時,這些數據不會丟失。3d
高速緩存(Cache):Cache 也是咱們常常見到的,它分爲一級緩存(L1 Cache)、二級緩存(L2 Cache)、三級緩存(L3 Cache)這些數據,它位於內存和 CPU 之間,是一個讀寫速度比內存更快的存儲器。當 CPU 向內存寫入數據時,這些數據也會被寫入高速緩存中。當 CPU 須要讀取數據時,會直接從高速緩存中直接讀取,固然,如須要的數據在Cache中沒有,CPU會再去讀取內存中的數據。指針
內存 IC 是一個完整的結構,它內部也有電源、地址信號、數據信號、控制信號和用於尋址的 IC 引腳來進行數據的讀寫。下面是一個虛擬的 IC 引腳示意圖blog
圖中 VCC 和 GND 表示電源,A0 - A9 是地址信號的引腳,D0 - D7 表示的是控制信號、RD 和 WR 都是好控制信號,我用不一樣的顏色進行了區分,將電源鏈接到 VCC 和 GND 後,就能夠對其餘引腳傳遞 0 和 1 的信號,大多數狀況下,+5V 表示1,0V 表示 0。索引
咱們都知道內存是用來存儲數據,那麼這個內存 IC 中能存儲多少數據呢?D0 - D7 表示的是數據信號,也就是說,一次能夠輸入輸出 8 bit = 1 byte 的數據。A0 - A9 是地址信號共十個,表示能夠指定 00000 00000 - 11111 11111 共 2 的 10次方 = 1024個地址。每一個地址都會存放 1 byte 的數據,所以咱們能夠得出內存 IC 的容量就是 1 KB。
若是咱們使用的是 512 MB 的內存,這就至關因而 512000(512 * 1000) 個內存 IC。固然,一臺計算機不太可能有這麼多個內存 IC ,然而,一般狀況下,一個內存 IC 會有更多的引腳,也就能存儲更多數據。
讓咱們把關注點放在內存 IC 對數據的讀寫過程上來吧!咱們來看一個對內存IC 進行數據寫入和讀取的模型
來詳細描述一下這個過程,假設咱們要向內存 IC 中寫入 1byte 的數據的話,它的過程是這樣的:
首先給 VCC 接通 +5V 的電源,給 GND 接通 0V 的電源,使用 A0 - A9 來指定數據的存儲場所,而後再把數據的值輸入給 D0 - D7 的數據信號,並把 WR(write)的值置爲 1,執行完這些操做後,便可以向內存 IC 寫入數據
讀出數據時,只須要經過 A0 - A9 的地址信號指定數據的存儲場所,而後再將 RD 的值置爲 1 便可。
圖中的 RD 和 WR 又被稱爲控制信號。其中當WR 和 RD 都爲 0 時,沒法進行寫入和讀取操做。
爲了便於記憶,咱們把內存模型映射成爲咱們現實世界的模型,在現實世界中,內存的模型很像咱們生活的樓房。在這個樓房中,1層能夠存儲一個字節的數據,樓層號就是地址,下面是內存和樓層整合的模型圖
咱們知道,程序中的數據不只只有數值,還有數據類型的概念,從內存上來看,就是佔用內存大小(佔用樓層數)的意思。即便物理上強制以 1 個字節爲單位來逐一讀寫數據的內存,在程序中,經過指定其數據類型,也能實現以特定字節數爲單位來進行讀寫。
下面是一個以特定字節數爲例來讀寫指令字節的程序的示例
這三個變量分別表示 1 個字節長度的 char,2 個字節長度的 short,表示4 個字節的 long。所以,雖然數據都表示的是 123,可是其存儲時所佔的內存大小是不同的。以下所示
這裏的 123 都沒有超過每一個類型的最大長度,因此 short 和 long 類型爲所佔用的其餘內存空間分配的數值是0,這裏咱們採用的是低字節序列的方式存儲
低字節序列:將數據低位存儲在內存低位地址。
高字節序列:將數據的高位存儲在內存地位的方式稱爲高字節序列。
指針是 C 語言很是重要的特徵,指針也是一種變量,只不過它所表示的不是數據的值,而是內存的地址。經過使用指針,能夠對任意內存地址的數據進行讀寫。
在瞭解指針讀寫的過程前,咱們先須要瞭解如何定義一個指針,和普通的變量不一樣,在定義指針時,咱們一般會在變量名前加一個 * 號。例如咱們能夠用指針定義以下的變量
實際上,這些數據表示的是從內存中一次讀取的字節數,好比 d e f 的值都爲 100,那麼使用 char 類型時就可以從內存中讀寫 1 byte 的數據,使用 short 類型就可以從內存讀寫 2 字節的數據, 使用 long 就可以讀寫 4 字節的數據,下面是一個完整的類型字節表
咱們能夠用圖來描述一下這個讀寫過程
數組是指多個相同的數據類型在內存中連續排列的一種形式。做爲數組元素的各個數據會經過下標編號來區分,這個編號也叫作索引,如此一來,就能夠對指定索引的元素進行讀寫操做。
首先先來認識一下數組,咱們仍是用 char、short、long 三種元素來定義數組,數組的元素用[value] 擴起來,裏面的值表明的是數組的長度,就像下面的定義
數組是內存的實現,數組和內存的物理結構徹底一致,尤爲是在讀寫1個字節的時候,當字節數超過 1 時,只能經過逐個字節來讀取,下面是內存的讀寫過程
數組是咱們學習的第一個數據結構,咱們都知道數組的檢索效率是比較快的,至於數組的檢索效率爲何這麼快並非咱們這篇文章討論的重點。
咱們上面提到數組是內存的一種實現,使用數組可以使編程更加高效,下面咱們就來認識一下其餘數據結構,經過這些數據結構也能夠操做內存的讀寫。
棧(stack)是一種很重要的數據結構,棧採用 LIFO(Last In First Out)即後入先出的方式對內存進行操做。它就像一個大的收納箱,你能夠往裏面放相同類型的東西,好比書,最早放進收納箱的書在最下面,最後放進收納箱的書在最上面,若是你想拿書的話, 必須從最上面開始取,不然是沒法取出最下面的書籍的。
棧的數據結構就是這樣,你把書籍壓入收納箱的操做叫作壓入(push),你把書籍從收納箱取出的操做叫作彈出(pop),它的模型圖大概是這樣
入棧至關因而增長操做,出棧至關因而刪除操做,只不過叫法不同。棧和內存不一樣,它不須要指定元素的地址。它的大概使用以下
隊列和棧很類似但又不一樣,相同之處在於隊列也不須要指定元素的地址,不一樣之處在於隊列是一種 先入先出(First In First Out) 的數據結構。隊列在咱們生活中的使用很像是咱們去景區排隊買票同樣,第一個排隊的人最早買到票,以此類推,俗話說: 先到先得。它的使用以下
與棧相對,FIFO 的方式表示隊列中最早所保存的數據會優先被讀取出來。
隊列的實現通常有兩種:順序隊列 和 循環隊列,咱們上面的事例使用的是順序隊列,那麼下面咱們看一下循環隊列的實現方式
環形緩衝區
循環隊列通常是以環狀緩衝區(ring buffer)的方式實現的,它是一種用於表示一個固定尺寸、頭尾相連的緩衝區的數據結構,適合緩存數據流。假如咱們要用 6 個元素的數組來實現一個環形緩衝區,這時能夠從起始位置開始有序的存儲數據,而後再按照存儲時的順序把數據讀出。在數組的末尾寫入數據後,後一個數據就會從緩衝區的頭開始寫。這樣,數組的末尾和開頭就鏈接了起來。
下面咱們來介紹一下鏈表和 二叉樹,它們都是能夠不用考慮索引的順序就能夠對元素進行讀寫的方式。經過使用鏈表,能夠高效的對數據元素進行添加 和 刪除操做。而經過使用二叉樹,則能夠更高效的對數據進行檢索。
在實現數組的基礎上,除了數據的值以外,經過爲其附帶上下一個元素的索引,便可實現鏈表。數據的值和下一個元素的地址(索引)就構成了一個鏈表元素,以下所示
對鏈表的添加和刪除都是很是高效的,咱們來敘述一下這個添加和刪除的過程,假如咱們要刪除地址爲 p[2] 的元素,鏈表該如何變化呢?
咱們能夠看到,刪除地址爲 p[2] 的元素後,直接將鏈表剔除,並把 p[2] 前一個位置的元素 p[1] 的指針域指向 p[2] 下一個鏈表元素的數據區便可。
那麼對於新添加進來的鏈表,須要肯定插入位置,好比要在 p[2] 和 p[3] 之間插入地址爲 p[6] 的元素,須要將 p[6] 的前一個位置 p[2] 的指針域改成 p[6] 的地址,而後將 p[6] 的指針域改成 p[3] 的地址便可。
鏈表的添加不涉及到數據的移動,因此鏈表的添加和刪除很快,而數組的添加涉及到數據的移動,因此比較慢,一般狀況下,使用數組來檢索數據,使用鏈表來進行添加和刪除操做。
二叉樹也是一種檢索效率很是高的數據結構,二叉樹是指在鏈表的基礎上往數組追加元素時,考慮到數組的大小關係,將其分紅左右兩個方向的表現形式。假如咱們把 50 這個值保存到了數組中,那麼,若是接下來要進行值寫入的話,就須要和50比較,肯定誰大誰小,比50數值大的放右邊,小的放左邊,下圖是二叉樹的比較示例
二叉樹是由鏈表發展而來,所以二叉樹在追加和刪除元素方面也是一樣有效的。
這一切的演變都是之內存爲基礎的。
做者簡介:cxuan 一個正在路上堅持信仰的技術人 最近他正在創做不少原創文章,都很是不錯。