0 、開篇:
(1)有十個地址信號引腳的內存IC(集成電路)能夠指定的地址範圍是多少?
0000000000-1111111111
2^10=1024
(2)高級編程語言中的數據類型表示的是什麼?
佔據內存區域的大小和存儲在該內存區域的數據類型
(3)在32位內存地址的環境中,指針變量的長度是多少位?
32位
(4)與物理內存有着相同的構造的數組的數據類型長度是多少?
1字節
(5)用LIFO方式進行數據讀寫的數據結構稱爲何?
棧
(6)根據數據的大小鏈表分叉位兩個方向的數據結構稱爲何?
二叉查找樹
一、內存的物理機制很簡單
① 下圖是內存IC的引腳配置示例
雖然這是一個虛擬的內存IC,但它的引腳和實際的內存IC是同樣的。VCC和GND是電源,A0~A9是地址信號的引腳,D0~D7是數據信號的引腳,RD和WR是控制信號的引腳。將電源鏈接到VCC和GND後,就能夠給其餘引腳傳遞好比0或者1這樣的信號。大多數狀況下,+5V的直流電壓表示1,0V表示0 。那麼,這個內存IC中能存儲多少數據呢?數據信號引腳有D0~D7共八個,表示一次能夠輸入輸出8位(=1字節)的數據。此外,地址信號引腳有A0~A9共十個,表示能夠指定0000000000~1111111111共1024個地址。而地址用來表示數據的存儲場所,所以咱們能夠得出這個內存IC中能夠存儲1024個1字節的數據,即爲1KB的容量。
說完容量,如今咱們來講說這個如何讀寫數據。
寫:
(1)VCC接入+5V;
(2)GND接入0V;
(3)使用A0~A9的地址信號來指定數據的存儲場所;
(4)再把數據的值輸入給D0~D7的數據信號;
(5)並把WR(write的簡寫)信號設定爲1;
執行完這些操做,就能夠在內存IC內部寫入數據,能夠見下圖:
讀:程序員
讀取數據時,只須要經過A0~A9的地址信號指定數據的存儲場所,而後再將RD(read的簡寫)信號設成1便可。執行完這些操做,指定地址中存儲的數據就會被輸出到D0~D7的數據信號引腳。以下圖:編程
二、內存的邏輯模型是樓房
雖然內存的實體是內存IC,不過從程序員的角度來看,也能夠把它假想成都存儲着數據的樓房,並不須要過多地關注內存的電源和控制信號等。咱們來看一下下圖,內存爲1KB時的表示方式(這裏地址的值是從上往下逐漸變大)數組
不過,在程序員眼裏的內存模型中,還包含着物理內存不存在的概念,那就是數據類型。編程語言中的數據類型表示存儲的是何種類型的數據,從內存上看,就是佔用的內存大小(佔有的樓層數)的意思。即便是物理上以1個字節位單位來逐一讀寫數據的內存,在程序中,經過制定其類型,也能實現以特定字節數位單位來進行讀寫。下面咱們來看一個具體的示例:數據結構
這3個變量的數據類型分別是,表示1字節長度的char,表示2字節長度的short,以及表示4字節長度的long。所以,雖然一樣是數據123,存儲時其所佔用的內存大小是不同的。這裏咱們假定採用的是將數據低位存儲在內存低位地址的低字節序方式。編程語言
假設一下,若是程序中只能逐個字節地對內存進行讀寫,那該多麼不便啊。在處理超過1個字節的數據時,還必需要編寫分割處理程序。此外,在不一樣的編程語言中,變量能夠指定的數據類型的最大長度也不一樣。函數
三、簡單的指針
接下來,咱們來看一下指針。指針是C語言的重要特徵。不少人都說指針難以理解,其實對已經閱讀到如今的各位讀者來講,指針應該很容易理解。理解指針的關鍵點就是要弄清楚數據類型這個概念。
指針也是一種變量,它表示的不是數據的值,而是存儲着數據的內存的地址。經過使用指針,就能夠對任意指定地址的數據進行讀寫。通常Windows計算機上使用的程序一般都是32位(4字節)的內存地址,這種狀況下,指針的變量的長度也是32位。看下圖的各類數據類型指針的定義:
假設d、e、f的值都是100 。在這種狀況下,使用d時就可以從編號100的地址中讀寫1個字節的數據,使用e時就是2個字節(100地址和101地址)的數據,使用f時就是4個字節(100地址~103地址)的數據。瞧,指針是否是很簡單,見下圖:3d
四、數組是高效使用內存的基礎
讓咱們回到主題「熟練使用有棱有角的內存」,在熟練以前,咱們先來看一下內存最直接的使用方法。在這裏,咱們要用到數組。
數組是指多個一樣數據類型的數據在內存中連續排列的形式。最做爲數組元素的各個數據會經過連續的編號被區分開來,這個編號稱爲索引(index)。指定索引後,就能夠對該索引所對應地址的內存進行讀寫操做(這個在第一章有講過:CPU是經過利用基址寄存器和變址寄存器來指定內存地址的)。索引和內存地址的變換工做則是由編譯器自動實現的。
char類型的數組以1個字節爲單位對內存進行讀寫;
short類型的數組以2個字節爲單位對內存進行讀寫;
long類型的數組以4個字節爲單位對內存進行讀寫。
之因此說數組是內存的使用方法的基礎,是由於數組和內存的物理構造是同樣的。特別是1字節類型的數組,它和內存的物理構造徹底一致。
使用數可以使編程工做變得更加高效。若是在反覆運行的循環處理中使用數組,很短的代碼就能達到按順序讀寫。不過,雖然是經過指定索引來使用數組,但這和內存的物理讀寫並無特別大的區別。所以不少程序都會在數組的使用上花費大量功夫,下面會介紹一些數組的變形方法。
五、棧、隊列以及環形緩衝區
棧和隊列對程序員來講再也熟悉不過了,它們均可以不經過指定地址和索引來對數組的元素進行讀寫。
棧和隊列的區別在於數據出入的順序是不一樣的。在對內存數據進行讀寫時,棧用的是LIFO(Last Input First Out,後入先出)方式,而隊列用的則是FIFO(First Input First Out,先入先出)方式。
若是要在程序中實現棧和隊列,就須要以適當的元素數來定義一個用來存儲數據的數組,以及對該數組進行讀寫的函數對。固然,在這些函數的內部,對數組的讀寫會涉及索引的管理,但從使用函數的角度來講,就沒有必要考慮數組及索引了。
這裏,咱們暫且把往棧中寫入數據的函數命名爲Push,把從棧中讀出數據的函數命名爲Pop,把往隊列中寫入數據的函數命名爲EnQueue,把從隊列中讀出數據的函數命名爲DeQueue。經過使用這些函數,能夠將數據臨時保存(寫入),而後再在須要時把這些數據讀出來:
在棧中,LIFO方式表示棧的數組中所保存的最後面的數據會被最早讀取出來,代碼運行後,按照12三、 456 、789的順序寫入的數據,結果卻按照789 、456 、123的順序被讀取出來。
隊列,FIFO方式表示隊列的數組中所 保存的最初數據會最早被讀取出來。上述代碼運行後,按照123 、 456 、789 的順序寫入的數據,結果會按照123 、456 、789 的順序被讀出來。
隊列通常是以環狀緩衝區(ring buffer)的方式來實現的,也就是本章標題中所說的「熟練使用有棱有角的內存」。例如,假設咱們要用6個元素的數組來實現一個隊列。這時能夠從數組的起始位置開始有序存儲數據,而後再按照存儲時的順序吧數據讀出。在數組的末尾寫入數據後,後一個額數據就會被寫入數組的起始位置(此時數據已經被讀出因此該位置是空的)。這樣,數組的末尾就和開頭鏈接了起來,數據的寫入和讀出也就循環起來了,以下圖:
六、鏈表使元素的追加和刪除更容易
接下來就介紹鏈表和二叉查找樹,都是不用考慮索引的順序就能夠對數組元素進行讀寫的方式。經過使用鏈表,能夠更加高效地對數組元素進行追加和刪除處理。而經過使用二叉查找樹,則能夠更加高效地對數組數據進行檢索。
在數組的各個元素中,除了數據的值以外,經過爲其附帶上下一個元素的索引,便可實現鏈表。數據的值和下一個元素的索引組合在一塊兒,就構成了數組的一個元素。
在須要追加或刪除數據的狀況下,使用鏈表使很高效的。下圖就是鏈表進行刪除的過程:
假設咱們刪除第3個元素,就能夠把第2個元素的「下一個元素:2」變成「下一個元素:3」便可。
下圖再展現一個如何往鏈表中追加數據:
假設要在第5個元素以前追加一個新數據。此時,咱們只須要在剛纔消除的第3個元素的位置中保存新的數據,並將第4個元素的「下一個元素:5」變動成「下一個元素:2」,以使新追加的元素的索引信息變成「下一個元素:5」便可。
鏈表與數組相比,最大的好處是中途刪除或追加元素時,其後的元素沒必要要進行所有的移動,而數組就必須移動其後所有的元素,這在高速計算機中也會花費很長時間,以下兩張圖:指針
七、二叉查找樹使數據搜索更有效
二叉查找樹是指在鏈表的基礎上往數組中追加元素時,考慮到數據的大小關係,將其分紅左右兩個方向的表現形式。例如,假設咱們事先把50這個值保存到了數組中,那麼若是接下來的值比先前保存的數值大的話,就要將其放到右邊,反之若是小的話就放在左邊。但實際的內存並不會分紅兩個方向,這是在程序邏輯上實現的。blog
在程序中如何實現呢,其實數組的每一個元素中只要有數據的值和兩個索引信息就能夠了。以下圖展現瞭如何用數組來實現二叉查找樹了。索引