《深入理解計算機系統原理》學習筆記與習題答案(一)

StevenOnesir發表於2020-11-25

第一章計算機系統漫遊
比較基礎廣泛,後面有空補上。
第二章 資訊的表示和處理
首先需要理解計算機當中的的概念。
位:在二進位制數系統中,位記為b,即位元,是最小的儲存單位。0/1中每一位是1 bit 。
本章中研究三種最重要的數字表示形式:

  1. 無符號編碼(unsigned)
  2. 補碼(two’s-complement)
  3. 浮點數(floating-point)

先有個粗略印象:無符號編碼實際上就是傳統的進位制轉換表示方式,由於只表示正數,正常轉換十進位制數到二進位制數就是無符號編碼;補碼用於表示有符號數(如負數),而浮點數編碼需要用到以2為基數的表示方法,後面會詳細介紹。

這裡補充介紹一下溢位(overflow)
資料型別超過了計算機字長界限就會發生溢位的情況。
書中舉例:500 * 400 * 300 * 200
有符號整型最大就是 0111 1111 1111 1111 1111 1111 1111 1111
一旦超過,自然發生正溢位成負數。
浮點數由於表示方法的不同,會溢位為正無窮。
整形表示精確但範圍較小,浮點數表示近似但範圍較大。
後面我們將在本章中學習計算機中數的表示方法、進位制之間表示方式的轉換以及直接運算元字的位級運算等,理解這些算數運算的特點、屬性。
補充: C語言標準:初始GNU 89->ANSI C->C90(C89)->C99->C11
2.1 資訊儲存
這一小節我們將瞭解計算機中的資訊儲存方式。
首先了解位元組的概念:大多數計算機中使用8位的塊作為一位元組(byte),是計算機中最小的存址單位。
計算機中將記憶體視為一個非常大的位元組陣列,稱為虛擬記憶體
記憶體中的每個位元組都有一個唯一的數字來標識,稱為地址
記憶體存值,自有地址,按址尋值。
所有可能的地址集合稱為虛擬地址空間(Virtual Address Space)。
需要注意虛擬地址空間虛擬記憶體的區別!!
程式物件:程式資料、指令與控制資訊。
程式物件的管理完全是在虛擬地址空間中進行的。
書中的舉例:C語言中每個指標的值都是指向某個儲存塊第一個位元組的地址。
核心:每個程式物件可以簡單地視為一個位元組塊,而程式本身就是一個位元組序列。
補充:指標的理解
指標提供了引用資料結構元素的機制。
指標包含值和型別,型別由編譯器維護,而機器級程式碼中並不體現。
值只是儲存對應物件的位置(往往某個儲存塊第一個位元組的位置)
型別則表示儲存物件的型別。

第三章中將結合指標在機器級程式碼上的表示與實現進行解釋。
2.1.1十六進位制表示法
這一節其實相當簡單,主要是進位制轉換與表示方法的記憶與理解。
掌握目標應該是形成主觀直覺,一眼能完成轉換。
十六進位制轉換不用在意大小寫,0-9+A-F來表示。
記憶幾個特殊的:
A:1010 C:1100 F:1111
由此易推出:
9:1001 B:1011 D:1101 E:1110
二進位制轉十六進位制就是每四位轉一個對應數字(包括A-F),並在開頭加上0x作為hex encode的標誌。
十六進位制轉二進位制就是每一個對應數字轉四位二進位制字元即可,也是快速且簡單的。
十進位制轉二進位制建議自行百度,初中知識。
有趣的一點在於最後除出來的餘數做高位的原因,大家可以悟一悟。
從除的次數更多的角度去想。
此節結束。
這裡建議完成練習題2.3

十進位制二進位制
00000 0000
1671010 0111
620011 1110
1881011 1100
550011 0111
1361000 1000
2431111 0011
820101 0010
1721010 1100
2311110 0111
二進位制十六進位制
0000 00000x00
1010 01110xA7
0011 11100x3E
1011 11000xBC
0011 01110x37
1000 10000x88
1111 00110xF3
0101 00100x52
1010 11000xAC
1110 01110xE7

2.1.2 字資料大小
字長(word size):是指明指標資料的標稱大小。
書上翻譯的概念顯得模糊而抽象。
實際上所謂指明指標資料的標稱大小即指標範圍的大小
直接理解為位元組的長度範圍即可。
字長主要影響的是虛擬地址空間,一個字長為w的機器虛擬地址的範圍為0~2^w-1,能訪問的位元組自然只有2 ^ w個,共2 ^ (w+3) bit
補充
32位:4GB
64位:16EB
三十二位程式可以在六十四位機器上執行,這叫做向後相容
六十四位程式只能在六十四位機器上執行。
所以區別“三十二位”還是“六十四位”程式的根據在於編譯方式,而不是執行機器。
關於為何64位機器不能用int型別儲存一個指標
簡單地說就是在六十四位機器中int佔4 byte,但儲存int的地址竟然是8 byte,超過了int的4 byte。

2.1.3 定址和位元組順序
首先我先用好理解的白話簡單講一講地址儲存的概念。
我們知道一個byte是8個bits,在計算機當中有一個叫虛擬空間地址的東西,每個對應的數(表示為十六進位制)如0x107,指向一個byte的儲存地址。而儲存一個變數,如int型別的指標指向的是該變數的起始儲存位置,如指標指向0x102,則0x103、0x104、0x105連續的三個bytes共同儲存該int型別變數,計算機當中位元組序列的儲存地址是連續的。
然後我們來講一講位元組順序,從我們的標題你也應該猜到了。
這裡就必須提及計算機界相當出名的兩個儲存順序:大端法和小端法
其實從名字入手就很好理解,如一個int型別變數的十六進位制值為:0x00230012,它的高位是00,低位是12。如果起始儲存位置從00開始存,就叫做大端法儲存;如果起始位置從12開始村,就叫做小端法儲存。Intel大多支援小端模式,而IBM與Oracle大多支援大端模式。
一般進行網路傳輸都會在傳輸前進行格式的正確轉換與表達,這樣不會出現衝突與錯誤。
大端小端的區別主要在三種情況下需要格外注意。
第一種就是網路傳輸不同機器的解釋規則不同易造成錯誤;
第二種就是閱讀機器級程式碼時提前知道該機器的儲存格式也非常重要,不然容易引發理解錯誤;
第三種就是當編寫規避正常的型別系統的程式時。

前面兩種都很好理解,第三種是什麼意思呢?
所謂規避正常的型別系統,實際上就是指型別的強制轉換。
轉換後十六進位制值會改變,但是在不同機器上的十六進位制值是相同的,但是不同機器上的指標值完全不同,並且這個時候大小端的不同表示可以很輕鬆地看出來。
原書中用了一段C的程式碼來演示這一過程。
其實真正的原因是:指標總是指向第一個位元組序列地址,如果表示方法不同,則指向同樣會不同(從頭到尾/從尾到頭)。
第三種有兩方面比較特別。一是可以通過(如書中原始碼)列印地址與變數值的方式確定機器儲存模式是大端還是小端儲存;二是通過指標值的不同體現出不同機器系統配置使用了不同的儲存分配規則。
可以類比第二種情況細細體會,列印的地址值與儲存值在你不知道機器型別、實際值大小時,你怎麼判斷真實值大小?
補充:
執行強制型別轉換時不會改變實際地址的值,只是重新告訴編譯器該用怎樣的資料型別來看待被該指標指向的資料。
使用命令 man ascii可以得到一張ascii碼錶(沒啥用)。
練習題 2.5
(後面的文章都不再貼出題目,請結合書本或者電子版觀看)

小端法大端法
2187
21 4387 65
21 43 6587 65 43

練習題 2.6
A:0000 0000 0011 0101 1001 0001 0100 0001
0100 1010 0101 0110 0100 0101 0000 0100
B:一共有21位相匹配
C:int的二進位制表示中除了前面11位以外都匹配,float的二進位制中除了最後2位與前面9位都匹配。
C更標準:int除了最高有效位其餘低位都匹配,float部分有效高位不匹配。

2.1.4 表示字串
其實就講了兩個細節:
第一是C語言字串會預設用null(其值為0)作為結尾。
第二是ascii碼編碼的字元相對於二進位制資料具有更強的平臺可移植性。

練習題 2.7
61 62 63 64 65 66 00

2.1.5 表示程式碼
講了一件事情:
不同系統與機器生成的二進位制檔案,也就是機器程式碼是不同的。
原因是不同機器型別使用不同的且不相容的指令和編碼方式。
所以就能理解為什麼一些檔案只能linux開啟了吧。。還得是64位。

2.1.6 布林代數簡介
其實就講了四種位運算。
~取非,^取異或,&取且,|取或。
所謂擴充套件到位向量,其實就是指可以一串二進位制資料可以進行上述操作罷了。
練習題 2.8

運算結果
a01101001
b01010101
~a10010110
~b10101010
a&b01000001
a|b01111101
a^b00111100

布林代數與布林環

位向量在進行布林運算過程中就形成了布林代數。
對於長度為2^w位的位向量,進行布林運算時我們將這種數學形式稱為布林環。布林環可以用集合的運算形式表示,|表示並集,&表示交集,^表示補集。
A:

顏色
黑色白色
藍色黃色
綠色紅紫色
藍綠色紅色
紅色藍綠色
紅紫色綠色
黃色藍色
白色黑色

B:

運算結果
藍色 | 綠色藍綠色
黃色 & 藍綠色綠色
紅色 ^ 紅紫色藍色

2.1.7 C語言中的位級運算
這裡主要講了一件事,C語言也能進行上面所說的位級運算。
還有a^a=0, a ^ b ^ a=b;
練習題 2.10

步驟*a*b
初始ab
第一步aa^b
第二步ba^b
第三步ba

練習題 2.11
A:first=last=k
B : 此時first=last,原理相當於a^a=0
C : 迴圈體中first<=last改為first<last

掩碼運算:顧名思義,就是遮蔽部分輸入位的運算。
如0x87654321 & 0xff = 0x00000021
十六進位制進行布林運算可先化為二進位制,注意位之間的關係。
練習題 2.12
A:x&oxFF
B: x ^ ~oxFF
C: x | oxFF
注意一個數^0則是本身, ^ 1 則是取反。
練習題 2.13
bool_or: int result = bis(x,y)
bool_xor: int result= bis(bic(x,y),bic(y,x))
注意一個點: x^y = (~x & y) | (x & ~y)
2.1.8 C語言中的邏輯運算
其實這一小節就講了邏輯運算與按位運算是不同的,相信寫程式的各位都能體會到。
第一個不同是邏輯運算只會返回True 或者 False兩種結果,只有在特殊情況(0x00 0x01)才會返回與按位運算相同的結果。
第二個不同就是邏輯運算存在短路,這一點可以融合到程式設計習慣中。

練習題 2.14

表示式
x&y0x20
xy
~x | ~yoxDF
x&!y0x00
x&&y0x01
x||yox01
!x||!y0x00
x&&~y0x01

這裡需要注意的有兩點:
1、C中兩個十六進位制(除0x00 0x01)做邏輯運算(&& ||)返回值為0x01;
2、注意邏輯運算的短路特點,同時~是按位非,!是邏輯非,返回值完全不同!

2.1.9 C語言中的移位運算
移位運算整體來講分兩種,左移與右移,左移只有一種情況,移動幾位右邊全部補0.如 00010011<<2 = 0100 1100
但是右移則分兩種情況:邏輯右移與算數右移
邏輯右移全部補0,算數右移全部補最高位的值。

C中無符號數全部採用邏輯右移,常數全部採用算數右移。
Java中>>是算數右移,>>>是邏輯右移。
同時C中儘量不要超過總位數移動,Java會嚴格取模,不存在問題。
最後注意運算先後順序,加法的優先順序都高於移位運算的優先順序。
基本上可以這樣計算:左移一位大兩倍,算數右移一位小兩倍。
練習題 2.16 略
(以後只會選取覺得有意義的題目,不然太浪費時間了。。。)

第二章2.1節到此結束,後面會抽空更新2.2節 整數表示。

相關文章