教材學習內容總結
1. 三種重要的數字表示
(1)無符號數、有符號數、浮點數
- 正數的原碼、反碼以及補碼是其本身。
- 負數的原碼是其本身,反碼是對原碼除符號位之外的各位取反,補碼則是反碼加1。
(2)為什么用補碼表示
- 能夠統一+0和-0的表示
- 采用原碼表示,+0的二進制表示形式為0 000 0000,而-0的二進制表示形式為1 000 0000;
- 采用反碼表示,+0的二進制表示形式為0 000 0000,而-0的二進制表示形式為1 111 1111;
- 采用補碼表示,+0的二進制表示形式為0 000 0000,而-0的二進制表示形式為1 111 1111+1=1 0000 0000,因為計算機會進行截斷,只取低8位,所以-0的補碼表示形式為0000 0000。
- 補碼的表示范圍比原碼和反碼表示的范圍都要大。用補碼能夠表示的范圍為-128~127,0~127分別用00000000~01111111來表示,而-127~-1則用10000001~11111111來表示,多出的10000000則用來表示-128。
- 對於有符號整數的運算能夠把符號位同數值位為一起處理
如果把符號位單獨考慮的話,CPU指令還要特意對最高位進行判斷,使計算機的最底層實現變得復雜。
(3)整數溢出漏洞
- 一個整數是一個固定的長度,它能存儲的最大值是固定的,當嘗試去存儲一個大於這個固定的最大值時,將會導致一個整數溢出。
- 整數溢出將會導致"不能確定的行為"。大多數編譯器會忽略整數溢出,致使整數溢出沒有立即察覺,因此沒有辦法去用一個應用程序來判斷先前計算的結果在實際上是否也是正確的,可導致某些類型的bugs,緩沖區溢出等。
2. 信息存儲
(1)十進制-二進制-八進制-十六進制轉換練習
(2)字
- 每個計算機都有一個字長,指明整數和指針數據的標稱大小。因為虛擬地址是以這樣的一個字來編碼的,所以字長最重要的系統參數就是虛擬地址空間的最大大小。
- 對於一個字長為w位的機器而言,虛擬地址的范圍為0~2^w-1,程序最多訪問2^w字節。
(3)gcc -m32/64
- 當沒有-m32或-m64參數時,一般情況下會生成跟操作系統位數一致的代碼
- gcc -m32 可以在64位機上(比如實驗樓的環境)生成32位的代碼
(4)字節順序
字節順序是網絡編程的基礎,是指占內存多於一個字節類型的數據在內存中的存放順序,通常有小端、大端兩種字節順序。
- 小端字節序指低字節數據存放在內存低地址處,高字節數據存放在內存高地址處。
- 大端字節序是高字節數據存放在低地址處,低字節數據存放在高地址處。
(5)布爾代數
- 邏輯運算
- 結果是1或0
- 所有邏輯運算都可以用與、或、非表達(最大式、最小式)而與或非可以用“與非”或“或非”表達,所以,只要一個與非門,就可以完成所有的邏輯運算。
- 邏輯與(&&) 遇0為0;
- 邏輯或(||) 遇1為1;
- 邏輯非 遇0為1,遇1為0;
- 位運算
- 結果是位向量
- 按位與(&) 二進制每一位遇0為0;
- 按位或(|) 二進制每一位遇1為1;
- 按位異或(^) 0^0=0,0^1=1,1^0=1,1^1=0;
- 按位取反(~) 二進制每一位取反。
- 掩碼運算 掩碼是位運算的重要應用,這里掩碼是一個特定位模式,表示從一個字中選擇一個位的集合。對特定位可以置一,可以清零。
- 邏輯運算與掩碼運算
- 邏輯運算結果是1或0,位運算結果是位向量
- 如果對第一個參數求值就能確定表達式的結果,邏輯運算符就不會對第二個參數求值。
3.整數表示
(1)整型數據類型
char 字符型數據,占用一個字節
unsigned char 無符號字符型數據,占用一個字節
short 短整形數據,占用兩個字節
unsigned short 無符號短整型數據,占用兩個字節
int 整形數據,占用兩個字節
unsigned int 無符號整型數據,占用兩個字節
long 長整型數據,占用四個字節
unsigned long 無符號長整型數據,占用四個字節
- 數據類型long long是在ISO C99中引入的。(編譯:gcc -std=c99)。
(2)無符號數的編碼
對於長度為w的位向量,都有一個唯一的值與之對應;反過來,在0~2^w-1之間的每一個整數都有一個唯一的長度為w的位向量二進制表示與之對應。
(3)補碼編碼
有符號整數最常見的表示方法:二進制補碼形式。
- 二進制補碼形式的三個特點:
- 1)二進制補碼的范圍是不對稱的:|TMin|=|TMax|+1,即不存在與最小值相對應的整數,這容易造成程序中細微的錯誤。
- 2)位數相同的前提下,無符號數的最大值剛好是二進制補碼最大值的2倍加1:UMax=2TMax+1。
3)二進制補碼中的-1與UMax有相同的位表示——全1位串。
ANSI C標准並未規定使用二進制補碼形式來表示有符號整數,但是幾乎所有的機器都是這么做的。補碼利用寄存器長度固定的特性簡化數學運算,將數學運算統一成加法,只要一個加法器就可以實現所有的數學運算。
(4)有符號整數的另外兩種標准表示方法
二進制反碼形式:與二進制補碼的表示方法類似,區別在於最高有效位的權值不同。
原碼:最高有效位是符號位,確定剩下的位取負權值還是正權值。
- 這兩種表示都有一個奇怪的屬性,即對於數字0存在兩種編碼。對於兩種方法,[00..0]都被解釋成+0 ,而-0在二進制反碼中表示為[11..1] ,在原碼中表示為[10..0]。
- 雖然曾經有過基於二進制反碼表示法的機器,但幾乎所有現代機器都使用二進制補碼。
- 符號數值編碼方式使用在浮點數的表示中。
(5)符號數與無符號數之間的轉換
ANSI C規定在無符號整數和有符號整數之間進行強制類型轉換時,並未改變對象的位模式,改變的是位模式的解釋方式。處理同樣字長的有符號數和無符號數之間相互轉換的一般規則是:數值可能會改變,位模式不變。
T2U:補碼到無符號數的轉換
U2T:無符號數到補碼的轉換
- 有符號數轉換為無符號數時,負數轉換為大的正數(可以理解為原值加上2的n次方),而正數保持不變。
- x<0 T2Uw(x)=x+2^w
- x>0 T2Uw(x)=x+2^w
(*w表示數據類型的位數)
- 無符號數轉換為有符號數時,對於小的數將保持原值,對於大的數則轉換為負數(可以理解為原值減去2的n次方)
- u<2^(w-1) U2Tw(u)=u
- u>=2^(w-1) U2Tw(u)=u-2^w
(*w表示數據類型的位數)
(6)C中有符號和無符號數
- 通常認為生命的常量是有符號的,要創建一個無符號常量,必須加上后綴字符'U'或者'u'。
- C語言允許無符號數和有符號數之間的轉換,轉換的原則是底層的位保持不變。
- 顯式轉換:強制類型轉換。
隱式轉換:一種類型的表達式被賦值給另外一種類型的變量時。
- C語言在處理同時包含無符號數和有符號數的表達式時,會隱式的將有符號數轉換為無符號數,並假設這兩個操作數都是非負的,然后執行運算。這一特性對於標准的算術運算來說無多大差異,但對於像"<"和">"這樣的關系運算符,有時會導致和直覺不相符的結果。
例:當一個有符號數“-1”與無符號數“0”用關系運算符連接時,會自動的將-1隱式轉換為無符號數4294967295(假設是一個使用補碼的32位機器),負數變成了正數,“-1<0u”的結果值就是0。
-C語言的強制類型轉換
(7)擴展一個數字的位表示
零擴展:要將一個無符號數轉換為一個更大的數據類型,只需簡單的最高位前加0。
符號擴展:將一個補碼數字轉換為一個更大的數據類型,在表示中添加最高有效位值的副本。
(8)截斷數字
截斷一個數字可能會改變其值,這也是值溢出的一種形式。,
無符號數x,將其截斷成k位,mod 2^k。
有符號數x,先將其看作無符號數截斷,然后在轉換成有符號數。
4. 整數運算
(1)無符號加法
- "字長膨脹"
- 在一系列連續的加法操作中,前一次的運算結果和當前操作數的和超出了當前字長的表示范圍,若要想正確保存本次的運算結果,必須增大字長。
- 持續的"字長膨脹"意味着要想完整的表示算術運算的結果,不能對字長做任何限制。一些編程語言,例如Lisp,支持無限精度的運算,允許任意字長的整數運算。更常見的是,編程語言只支持固定精度(字長)的運算。
- 無符號運算可以被視為某種形式的模運算。無符號加法等價與計算取模后的和,可以通過簡單的丟棄超出字長的部分來完成取模計算。
- 算術運算溢出
- 指完整的整數結果無法放到數據類型的字長限制中。
- 執行C程序時,不會將溢出作為錯誤而發出警告信號。
- 判斷無符號運算是否溢出,例如s=x+y(s、x、y均為無符號數),則唯一可靠的判斷標准就是s<x或s<y。
- 模數加法構成了一種數學結構——阿貝爾群。可交換、可結合、單位元0、每個元素有加法逆元。
(2)二進制補碼加法
- 大多數計算機使用同樣的機器指令來執行無符號或者有符號加法。
- 二進制補碼加法在溢出時同樣會將結果中超過字長的部分丟棄,然而,這樣的結果在數學上卻能用取模來等同。
以z=x+y為例,其中x,y,z均為位長為n的有符號數
- 正溢出:x+y >= 2^(w-1)
z=x+y-2^w x,y為正值,而最終結果z為負值。
- 負溢出:x+y < -2^(w-1)
z=x+y-2^w x,y為負值,而最終結果z為正值。
(3)補碼的非
- 補碼非運算
對於范圍在[-2^(w-1),2^(w-1))中的x,補碼的非運算有如下兩種情況:
x=-2^(w-1)時,為-2^(w-1)
x>-2^(w-1)時,為-x
- 位級補碼非
- 對每一位求補,再對結果+1
- 設k為最右面的1的位置,將k左邊的所有位取反。
(4)乘法
- C語言中的無符號乘法被定義為對整數乘積結果進行低位截斷后的值,即等價於對乘法結果進行取模運算。
- C語言中的有符號乘法是通過將2w位的乘積截斷為w位的方式實現的,即mod 2^w。
對於無符號和補碼乘法來說,乘法運算的位級表示都是一樣的。
(5)常數乘法
- 在大多數機器上,乘法指令和加法、減法、位運算、移位等指令相比要慢很多。因此,編譯器使用的一項重要優化措施就是試圖用移位和加法的組合來代替乘以常數因子的乘法。
- 常數為2的k次冪的時,直接左移k位即可。
- 常數不是2的整數次冪的時,將常數C表示為2的幾個整數次冪的和,結合移位運算和加法運算。
- 對無符號變量x
x<<k 等價於x*2^k。 (特別地,我們可以用1U<<k來計算2^k)
- 對二進制補碼數x
x<<k 等價於x*2^k。
- 即使在運算結果溢出時,上述等價關系依然成立。
(6)除以2的冪
- 機器運算中,除法比乘法更慢。當被除數為2的整數次冪時,通過右移來解決。右移時需要區分無符號數和補碼。
注意:整數除法總是舍入到零
- 無符號數——邏輯右移:無符號數除以2的k次冪,就等同於對其邏輯右移k位。
- 補碼——算術右移:補碼進行算術左移時,需要考慮補碼數的正負,因為整數除法總是舍入到零,無符號數中沒有負數不必擔心,但補碼中有正有負,正數向下舍入到零,負數應該向上舍入到零。所以這里涉及到在移位前偏置。
- 即:x≥0時,除以2的k次冪等價於將x算術右移k位
x<0時,先將x加上(2^k)-1,再算術右移k位
- 與乘法不同,這種右移方法不能推廣到任意常數C。
(7)整數運算的思考
- 計算機執行的“整數運算”實際上是一種模運算。
- 表示數字的有限字長限制了可能的值的取值范圍。
- 無論運算數是以無符號形式還是補碼形式表示,都有完全一樣或者非常類似的位級行為。
5. 浮點數
(1)概述
- 浮點表示對形如 V=x*2^y 的有理數進行編碼
- 適用於:非常大的數字(|V|>>0)、非常接近於0的數字(|V|<<1)、實數運算的近似值。
- IEEE浮點標准:IEEE標准754
(2)二進制小數
- 二進制點左邊第i位,權為2^i;右邊第i位,權為(1/2)^i。
- 增加二進制表示的長度可以提高表示的精度。
(3)IEEE浮點格式
- IEEE浮點標准
表示一個數: V=(-1)^s * M * 2^E
- 符號:s決定這個數是正還是負。0的符號位特殊情況處理。
- 尾數:M是一個二進制小數,范圍為1~2-ε或者0~1-ε,ε=(1/2)^n.
- 階碼:E對浮點數加權,權重是2的E次冪(可能為負數)。
- 浮點數的位表示划分為三個字段,分別對這些值進行編碼
- 一個單獨的符號位 s 直接編碼符號 s.
- k位的階碼字段 exp = e(k-1)……e1e0編碼階碼 E.
- n位小數字段 frac = f(n-1)……f1f0編碼尾數 M,但是編碼出來的值也依賴於階碼字段的值是否等於0.
- 浮點數可分為三種表達方式
- 規格化值
- 非規格化值
- 特殊值
a)規格化的值
exp的位模式既不全0也不全1的時候,這是最一般最普遍的情況,因而是規格化的。
- 階碼字段被解釋為以偏置形式表示的有符號整數。
階碼: E = e-Bias
- e:無符號整數
- Bias:偏置值,Bias=[2^(k-1)-1]
- 小數字段frac的解釋為描述小數值f,二進制小數點在小數字段最高有效位的左邊。
- 尾數 M = 1+f(有時也可稱作隱含的以1開頭的表示)
b)非規格化的值
階碼域全為0時的數。
- 階碼: E = 1-Bias
- 尾數: M = f(小數字段的值,不包含隱含的1)
- 功能:
- 提供了一種表示數值0的方法。
- 表示那些非常接近0.0的數,提供一種“逐漸溢出”的屬性。
c)特殊值
特殊值是在階碼位全為1的時候出現的。
- 小數域全為0時,數值用於表示無窮:符號位為0表示正無窮,符號位為1表示負無窮。
- 小數域非全0時,數值用於表示"NaN"(Not a Number)。這樣的數值用於表示諸如對(-1)^0.5這樣無意義的結果。
(4)舍入
- 因為表示方法限制了浮點數的范圍和精度,浮點運算只能近似的表示實數運算。
- 舍入運算的任務:找到和數值x最接近的匹配值x',可以用期望的浮點形式表示出來。
- IEEE浮點格式定義了四種不同的舍入方法:
- 向偶舍入(默認) 將數字向上或向下舍入,是的結果的最低有效數字為偶數。能用於二進制小數。
- 向零舍入 把整數向下舍入,負數向上舍入。
- 向下舍入 正數和負數都向下舍入。
向上舍入 正數和負數都向上舍入。
- 默認的(即向偶舍入)方法可以得到最接近的匹配,其余三種產生實際值的確界。
(5)浮點運算
- 浮點加法
- - 可交換
- - 不具結合性:缺少的重要群屬性
- - 大多數值的浮點加法都有逆元,除了無窮和NaN。
- 滿足單調性
- 浮點乘法
- - 可交換的
- - 不具有結合性:可能發生溢出,或由於舍入而失去精度。
- - 乘法單位元為1.0
- - 在加法上不具備分配性
- 在一定條件下滿足單調性(無符號或補碼的乘法沒有這些單調性屬性)
(6)C語言中的浮點數
- C提供兩種浮點類型:float和double,在支持IEEE浮點標准的機器上分別表示單精度和雙精度浮點數。
- C標准並未明確要求機器在浮點數的表示上采取IEEE標准 。
- int、float、double之間的強制類型轉換
- - int → float 數字不會溢出,但有可能舍入
- - int/float → double 保留精確的數值
- - double → float 由於精確度變小,可能溢出為±∞,也有可能被舍入
- float/double → int 可能向零舍入,可能溢出。
遇到的問題及解決
1. Perl語言的代碼到底是如何編譯運行的?!
我在網上查的是:
- 第一步,創建XXX.pl並編輯
- 第二步,perl XXX.pl
- 第三步,運行./XXX.pl
但是我用這種方法敲了P24的代碼:
開始的報錯是因為“i++”前面少了“$”。
提示沒有權限,我百度了之后說是用 chmod +x XXX.pl 解決,然后再次運行,顯示沒有文件目錄,是因為我按照書上打上去的,把它改成#!/home/shiyanlou,但是還是權限不夠。不清楚它到底是怎么運行起來的...
2.不理解什么叫“數值可能改變,但是位模式不變。”
學習心得體會:
書上的公式有些不能理解是怎么推導的,這周任務量大時間緊,博客弄得挺匆忙,代碼托管同樣,我在評論上補充上去。
節日快樂!