學習計時:共8小時
讀書:2h
代碼:1h
做業:3h
博客:2h
---程序員
使用C99特性時 gcc -std=c99 xxx.c 實驗樓環境爲64位,編譯爲32位機器碼: gcc -m32 xxx.c。編程
1.十六進制(簡寫爲「hex」)使用數字‘0’~‘9’,以及字符‘A’~‘F’來表示16 個可能的值。 2.二進制與十六進制的轉換 假設數字0x173A4C,能夠經過展開每一個十六進制數字,將它轉換爲二進制格式,以下所示: 十六進制 1 7 3 A 4 C 二進制 0001 0111 0011 1010 0100 1100 這樣就獲得了二進制表示000101110011101001001100。 反過來,若是給定一個二進制數字1111001010110110110011,你能夠首先把它分爲每4 位一組,再把它轉換爲十六進制。不過要注意,若是位的總數不是4的倍數,最左邊的一組能夠少於4 位,前面用0 補足,而後將每一個4 位組轉換爲相應的十六進制數字: 二進制 11 1100 1010 1101 1011 0011 十六進制 3 C A D B 3 3.十進制與十六進制的轉換
每臺計算機都有一個字長(word size), 對於一個字長爲w 位的機器而言,虛擬地址的範圍爲0 ~ 2w-1,程序最多 訪問2w 個字節。
1. C 語言中數字數據類型的字節數 C 聲明 32 位機器 64 位機器 char 1 1 short int 2 2 int 4 4 long int 4 8 long long int 8 8 char * 4 8 float 4 4 double 8 8 2.C 語言標準對不一樣數據類型的數字範圍設置了下界,可是卻沒有上界。
1.在幾乎全部的機器上,多字節對象都被存儲爲連續的字節序列,對象的地址爲使用字節中最小的地址。例如,假設一個類型爲int的變量x的地址爲0x100,也就是說,地址表達式&x 的值爲0x100。那麼,x的4個字節將被存儲在存儲器的0x100、0x10一、0x102和0x13 位置。 2.排列表示一個對象的字節有兩個通用的規則。考慮一個w位的整數,位表示爲[xw-1,xw-2,…,x1,x0],其中xw-1 是最高有效位,而x0 是最低有效位。假設w是8 的倍數,這些位就能被分組成爲字節,其中最高有效字節包含位[xw-1,xw-2,…,xw-8],而最低有效字節包含位[x7,x6,…,x0],其餘字節包含中間的位。某些機器選擇在存儲器中按照從最低有效字節到最高有效字節的順序存儲對象,而另外一些機器則按照從最高有效字節到最低有效字節的順序存儲。前一種規則—最低有效字節在最前面的方式,稱爲小端法(little endian)。大多數Intel 兼容機都採用這種規則。後一種規則—最高有效字節在最前面的方式,稱爲大端法(big endian)。大數IBM 和Sun Microsystems 的機器都採用這種規則。 3.C 語言中的typedef聲明提供了一種給數據類型命名的方式。這可以極大地改善代碼的可讀性。
1.C 語言中字符串被編碼爲一個以null(其值爲0)字符結尾的字符數組。每一個字符都由某個標準編碼來表示,最多見的是ASCII 字符碼。 2.在使用ASCII 碼做爲字符碼的 任何系統上都將獲得相同的結果,與字節順序和字大小規則無關。於是,文本數據比二進制數據具備更強的平臺獨立性。
1.不一樣的機器類型使用不一樣的且不兼容的指令和編碼方式 2.即便是徹底同樣的進程運行在不一樣的操做系統上也會有不一樣的編碼規則,所以二進制代碼是不兼容的
咱們能夠將上述4 個布爾運算擴展到位向量的運算,位向量就是有固定長度爲w、由0 和1組成的串。位向量的運算能夠定義成參數的每一個對應元素之間的運算。假設a 和b 分別表示位向量[aw-1,aw-2,…,a0]和[bw-1,bw-2,…,b0]。
1.C 語言的一個頗有用的特性就是它支持按位布爾運算。事實上,咱們在布爾運算中使用的那些符號就是C 語言所使用的:| 就是OR(或),& 就是AND(與),~ 就是NOT(取反),而^就是EXCLUSIVE-OR(異或)。這些運算能運用到任何「整型」的數據類型上,也就是那些聲明爲char或者int的數據類型,不管它們有沒有像short、long、long long 或者unsigned這樣的限定詞。 2.位級運算的一個常見用法就是實現掩碼運算,這裏掩碼是一個位模式,表示從一個字中選出的位的集合。讓咱們來看一個例子,掩碼0xFF(最低的8位爲1)表示一個字的低位字節。位級運算x&0xFF生成一個由x的最低有效字節組成的值,而其餘的字節就被置爲0。好比,對於x=0x89ABCDEF,其表達式將獲得0x000000EF。表達式~0 將生成一個全1 的掩碼,無論機器的字大小是多少。儘管對於一個32位機器來講,一樣的掩碼能夠寫成0xFFFFFFFF,可是這樣的代碼不是可移植的
C 語言還提供了一組邏輯運算符 ||、&& 和!,分別對應於命題邏輯中的OR、AND和NOT運算。邏輯運算很容易和位級運算相混淆,可是它們的功能是徹底不一樣的。邏輯算認爲全部非零的參數都表示TRUE,而參數0 表示FALSE。它們返回1 或者0,分別表示結果爲TRUE 或者爲FALSE。
下面的表給出了對某些實例8 位數據作不一樣的移位操做獲得的結果。 操做值 參數x [01100011] [10010101] x << 4 [00110000] [01010000] x >> 4(邏輯右移) [00000110] [00001001] x >> 4(算術右移) [00000110] [11111001] C 語言標準並無明肯定義應該使用哪一種類型的右移。對於無符號數據(也就是以限定詞 unsigned 聲明的整型對象),右移必須是邏輯的。而對於有符號數據(默認的聲明的整型對象),算術的或者邏輯的右移均可以。不幸的是,這就意味着任何假設一種或者另外一種右移形式的代碼都潛在着可移植性問題。然而,實際上,幾乎全部的編譯器/ 機器組合都對有符號數據使用算術右移,且許多程序員也都假設機器會使用這種右移。另外一方面,Java 對於如何進行右移有明確的定義。表達式x>>k 會將x 算術右移k 個位置,而x>>>k 會對x 作邏輯右移。 在C 表達式中搞錯優先級是一種常見的程序錯誤,並且經常很難檢查出來。因此當拿不許的時候,加上括號!
1.數據類型int 能夠用2個字節的數字來實現,而這幾乎退回到了16位機器的時代。還看到,long的大小能夠用4 個字節的數字來實現,而實際上也經常是這樣。數據類型long long是在ISO C99 中引入的,它須要至少8 個字節表示。 2.C、C++ 和Java 中的有符號和無符號數 C 和C++ 都支持有符號(默認)和無符號數。Java 只支持有符號數。
無符號數的二進制表示有一個很重要的屬性,就是每一個介於0 ~ 2w-1之間的數都有惟一一個w 位的值編碼
對於許多應用,咱們還但願表示負數值。最多見的有符號數的計算機表示方式就是補碼(two’s-complement)形式。在這個定義中,將字的最高有效位解釋爲負權(negative weight)。咱們用函數B2Tw來表示 咱們能夠看出B2Tw 是一個從長度爲w的位模式到TMinw和TMax之間數字的映射,寫作B2Tw:{0,1}w → {-2w-1,…,2w-1-1}。同無符號表示同樣,在可表示的取值範圍內的每一個數字都有一個惟一的w位的補碼編碼。用數學語言來講就是B2Tw是一個雙射—每一個長度爲w 的位向量都對應一個惟一的值;反過來,每一個介於-2w-1和2w-1-1 之間的整數都有一個惟一的長度爲w的位向量二進制表示。

1.C 語言支持全部整型數據類型的有符號和無符號運算。儘管C 語言標準沒有指定有符號數要採用某種表示,可是幾乎全部的機器都使用補碼。常,大多數數字都默認爲是有符號的。例如,當聲明一個像12345或者0x1A2B這樣的常量時,這個值就被認爲是有符號的。要建立一個無符號常量,必須加上後綴字符‘U’或者‘u’。例如,12345U 或者0x1A2Bu 2.C 語言容許無符號數和有符號數之間的轉換。轉換的原則是底層的位表示保持不變。所以,在一臺採用補碼的機器上,當從無符號數轉換爲有符號數時,效果就是應用函數U2Tw,而從有符號數轉換爲無符號數時,就是應用函數T2Uw,其中w表示數據類型的位數。
一種常見的運算是在不一樣字長的整數之間轉換,同時又保持數值不變。固然,當標數據類型過小以致於不能表示想要的值時,這根本就是不可能的。然而,從一個較小的數據類型轉換到一個較大的類型,這應該老是可能的。將一個無符號數轉換爲一個更大的數據類型,咱們只須要簡單地在表示的開頭添加0,這種運算稱爲零擴展(zero extension)。將一個補碼數字轉換爲一個更大的數據類型能夠執行符號擴展(sign extension),規則是在表示中添加最高有效位的值的副本。由此可知,若是咱們原始值的位表示爲[xw-1,xw-2,…,x0],那麼擴展後的表示就爲[xw-1,…,xw-1,xw-1,xw-2,…,x0]。(咱們用淺灰色標出符號位xw-1來突出它們在符號擴展中的角色。)
咱們不用額外的位來擴展一個數值,而是減小表示一個數字的位數。 在一臺典型的32 位機器上,當把x 強制類型轉換爲short 時,咱們就將32 位的int 截斷爲16 位的shortint。就像前面所看到的,這個16位的位模式就是-12 345 的補碼錶示。當咱們把它強制類型轉換回int時,符號擴展把高16位設置爲1,從而生成-12 345 的32 位補碼錶示
就像咱們看到的那樣,有符號數到無符號數的隱式強制類型轉換致使了某些非直觀的行爲。而這些非直觀的特性常常致使程序錯誤,而且這種包含隱式強制類型轉換細微差異的錯誤很難被發現。由於這種強制類型轉換是在代碼中沒有明確指示的狀況下發生的,程序員常常忽視了它的影響。 咱們已經看到了因爲許多無符號運算的細微特性,尤爲是有符號數到無符號數的隱式轉換,會致使錯誤或者漏洞的方式。避免這類錯誤的一種方法就是毫不使用無符號數。實際上,除了C之外,不多有語言支持無符號整數。很明顯,這些語言的設計者認爲它們帶來的麻煩要比益處多得多。例如,Java只支持有符號整數,而且要求用補碼運算來實現。正常的右移運算符>>被定義爲執行算術右移。特殊的運算符>>> 被指定爲執行邏輯右移。當咱們想要把字僅僅看作是位的集合,而且沒有任何數字意義時,無符號數值是很是有用的。例如,往一個字中放入描述各類布爾條件的標記(flag)時,就是這樣。地址天然地就是無符號的,因此係統程序員發現無符號類型是頗有幫助的。當實現模運算和多精度運算的數學包時,數字是由字的數組來表示的,無符號值也會很是有用。
無符號運算能夠被視爲一種模運算形式。無符號加法等價於計算和模上2w。能夠經過簡單的丟棄x + y 的w + 1 位表示的最高位,來計算這個數值。 通常而言,咱們能夠看到,若是x+y<2w,則和的w+1位表示中的最高位會等於0,所以丟棄它不會改變這個數值。另外一方面,若是2w≤x+y<2w+1,則和的w+1位表示中的最高位會等於1,所以丟棄它就至關於從和中減去了2w。
對於補碼加法,咱們必須肯定當結果太大(爲正)或者過小(爲負)時,應該作些什麼。給定在範圍-2w-1 ≤ x, y ≤ 2w-1-1 以內的整數值x和y,它們的和就在範圍-2w≤x+y≤2w-2以內,要想準確表示,可能須要w+1 位。
範圍在0 ≤ x, y ≤ 2w-1 內的整數x 和y 能夠表示爲w 位的無符號數,可是它們的乘積x · y 的取值範圍爲0 到(2w-1)2 = 22w-2w+1+1 之間。這可能須要2w 位來表示。不過,C 語言中的無符 號乘法被定義爲產生w 位的值,就是2w 位的整數乘積的低w 位表示的值。
範圍在-2w-1 ≤ x, y ≤ 2w-1-1 內的整數x 和y 能夠表示爲w 位的補碼數字,可是它們的乘積 x · y 的取值範圍在-2w-1 · (2w-1-1) = -22w-2+2w-1 和-2w-1 · -2w-1 = -22w-2 之間。要用補碼來表示這個乘積,可能須要2w 位—大多數狀況下只須要2w-1位,可是特殊狀況22w-2須要2w位(包括一個符號位0)。然而,C 語言中的有符號乘法是經過將2w 位的乘積截斷爲w位的方式實現的。
在大多數機器上,整數乘法指令至關慢,須要10個或者更多的時鐘週期,然而其餘整數運算(例如加法、減法、位級運算和移位)只須要1個時鐘週期。所以,編譯器使用了一項重要的優化,試着用移位和加法運算的組合來代替乘以常數因子的乘法。首先,咱們會考慮乘以2 的冪的狀況,而後再歸納成乘以任意常數。
在大多數機器上,整數除法要比整數乘法更慢—須要30個或者更多的時鐘週期。除以2的冪也能夠用移位運算來實現,只不過咱們用的是右移,而不是左移。無符號和補碼數分別使用邏輯移位和算術移位來達到目的
正如咱們看到的,計算機執行的「整數」運算其實是一種模運算形式。表示數字的有限字長限制了可能的值的取值範圍,結果運算可能溢出。咱們還看到,補碼錶示提供了一種既能表示負數也能表示正數的靈活方法,同時使用了與執行無符號算術相同的位級實現,這些運算包括加法、減法、乘法,甚至除法,不管運算數是以無符號形式仍是以補碼形式表示的,都有徹底同樣或者很是相似的位級行爲。 咱們看到了C 語言中的某些規定可能會產生使人意想不到的結果,而這些多是難以察覺和理解的缺陷的源頭。咱們特別看到了unsigned數據類型,雖然它概念上很簡單,但可能致使即便是資深程序員都意想不到的行爲
十進制表示法
IEEE 浮點標準用V = (-1)s × M × 2E 的形式來表示一個數: • 符號(sign) s 決定這個數是負數(s=1)仍是正數(s=0),而對於數值0 的符號位解釋做 爲特殊狀況處理。 • 尾數(significand) M 是一個二進制小數,它的範圍是1 ~ 2-ε,或者是0 ~ 1-ε。 • 階碼(exponent) E 的做用是對浮點數加權,這個權重是2 的E 次冪(多是負數)。 將浮點數的位表示劃分爲三個字段,分別對這些值進行編碼: • 一個單獨的符號位s 直接編碼符號s。 • k 位的階碼字段exp = ek-1…e1e0 編碼階碼E。 • n 位小數字段frac = fn-1…f1 f0 編碼尾數M,可是編碼出來的值也依賴於階碼字段的值是否 等於0。
當處理負數時,有一個小的難點,由於它們有開頭的1,而且它們是按照降序出現的,可是不須要浮點運算來進行比較也能解決這個問題 有k 位階碼和n 位小數的浮點表示的通常屬性。 • 值+0.0 總有一個全爲0 的位表示。 • 最小的正非規格化值的位表示,是由最低有效位爲1 而其餘全部位爲0構成的。它具備小數(和尾數)值M = f = 2-n 和階碼值E = -2k-1 + 2。所以它的數字值是V = 2-n-2k-1+2。 • 最大的非規格化值的位模式是由全爲0的階碼字段和全爲1的小數字段組成的。它有小數(和尾數)值M = f = 1-2-n(咱們寫成1-ε)和階碼值E = -2k-1 + 2。所以,數值V = (1-2-n)× 2-n-2k-1+2,這僅比最小的規格化值小一點。 • 最小的正規格化值的位模式的階碼字段的最低有效位爲1,其餘位全爲0。它的數值M =1,而階碼值E = -2k-1 + 2
由於表示方法限制了浮點數的範圍和精度,浮點運算只能近似地表示實數運算。所以,對於值x,咱們通常想用一種系統的方法,可以找到「最接近的」匹配值x',它能夠用指望的浮點形式表示出來。這就是舍入(rounding)運算的任務。一個關鍵問題是在兩個可能值的中間肯定舍入方向。
IEEE 標準中指定浮點運算行爲方法的一個優點在於,它能夠獨立於任何具體的硬件或者軟件實現。所以,咱們能夠檢查它的抽象數學屬性,而沒必要考慮實際上它是如何實現的。
全部的C 語言版本提供了兩種不一樣的浮點數據類型:float 和double。在支持IEEE 浮點格式的機器上,這些數據類型就對應於單精度和雙精度浮點。另外,這類機器使用向偶數舍入的方式。不幸的是,由於C語言標準不要求機器使用IEEE浮點,因此沒有標準的方法來改變舍入方式或者獲得諸如-0、+∞、-∞或者NaN之類的特殊值。大多數系統提供include(‘.h’)文件和讀取這些特徵的過程庫,可是細節由於系統不一樣而不一樣
1.計算機將信息按位編碼,一般組織成字節序列。用不一樣的編碼方式表示整數、實數和字符串。不一樣的計算機模型在編碼數字和多字節數據中的字節排序時使用不一樣的約定。 2.C 語言的設計能夠包容多種不一樣字長和數字編碼的實現。雖然高端機器逐漸開始使用64 位字長,可是目前大多數機器仍使用32位字長。大多數機器對整數使用補碼編碼,而對浮點數使用IEEE浮點編碼。在位級上理解這些編碼,而且理解算術運算的數學特性,對於想使編寫的程序能在所有數值範圍上正確運算的程序員來講,是很重要的。 3。在相同長度的無符號和有符號整數之間進行強制類型轉換時,大多數C語言實現遵循的原則是底層的位模式不變。在補碼機器上,對於一個w位的值,這種行爲是由函數T2Uw 和U2Tw來描述的浮點表示經過將數字編碼爲x×2y的形式來近似地表示實數。最多見的浮點表示方式是由IEEE標準754定義的。它提供了幾種不一樣的精度,最多見的是單精度(32 位)和雙精度(64位)。IEEE 浮點也可以表示特殊值+ ∞、- ∞和NaN。必須很是當心地使用浮點運算,由於浮點運算只有有限的範圍和精度,並且不遵照廣泛的算術屬性,好比結合性。
公式能夠不看,習題不能不作,考覈題目和課後習題相似,重點題目:數組
2.四、2.六、2.八、2.十一、2.1三、2.1四、2.1八、2.1九、2.2一、2.2三、安全
2.2四、2.2五、2.2七、2.2九、2.3三、2.3四、2.3九、2.40、2.4二、2.4三、markdown
2.4四、2.4五、2.4七、2.50、2.5二、2.54網絡
p20: 三種數字:無符號數、有符號數(2進制補碼)、浮點數,信息安全系同窗從逆向角度考慮爲何會產生漏洞編輯器
p22: 進制轉換,注意拿二進制做中間結果就好轉了函數
p25: gcc -m32 能夠在64位機上(好比實驗樓的環境)生成32位的代碼學習
p26: 字節順序是網絡編程的基礎,記住小端是「高對高、低對低」,大端與之相反就能夠了。測試
p28: 代碼執行一下
p32: 能區分邏輯運算(結果是1或0)和位運算(結果是位向量),全部邏輯運算均可以用與、或、非表達(最大式、最小式),而與或非能夠用「與非」或「或非」表達,因此,只要一個與非門,就能夠完成全部的邏輯運算。
p33: 掩碼是位運算的重要應用,對特定位能夠置一,能夠清零
p38: 要用C99中的「long long」類型,編譯是要用 gcc -std=c99
p39: 補碼的利用寄存器的長度是固定的特性簡化數學運算。想一想鐘錶,12-1 等價於 12 + 11,利用補碼能夠把數學運算統一成加法,只要一個加法器就能夠實現全部的數學運算。
p44: 注意C語言中有符號數和無符號數的轉換規則,位向量不變。想一想第一章說的 信息就是「位+上下文」
p48: 怎麼樣讓負數等於正數? 信息安全的逆向思惟
p49: 0擴展和符號擴展
p52: 深刻思考一下代碼和結果
p54: 如何讓整數運算溢出?如何避免? p62例子看看
p67: 關於整數運算的最後思考
p67: 浮點數有科學計數法的基礎就不難理解,IEEE標準754
p68: 浮點數運算的不精確性與舍入
p70: IEEE浮點標準,float/double類型
p74: 整數與浮點數表示同一個數字的關係
p78: 整數與浮點數轉換規則
p80:家庭做業能夠選作,協調好每題最多兩人一組作,一星題目一人加一分,二星加二分,三星加三分,四星加四分
p24 進制轉換代碼
p24 show_bytes,寫個main函數測試一下,參考p30代碼
p35 練習2.11, 能夠用GDB單步跟蹤一下,理解更深入
p44 代碼放到一個main函數中,能夠用GDB單步跟蹤一下,理解更深入
p47/p49代碼放到一個main函數中,能夠用GDB單步跟蹤一下,理解更深入
p78,轉換規則能夠寫幾行代碼測試一下。
做業 小夥伴:20135303
由於本題受12次操做的限制,故不能按位計算是否該位爲1。考慮到本題只須要判斷1的個數的奇偶性,而並不須要計算一共有多少個1。那麼咱們考慮到若是能去掉偶數個1對結果並不會產生影響,這須要快速的去掉偶數個1。由於異或運算剛好能夠把同爲1時變成0。而後在利用分治的方法,總體異或來減小操做次數。
操做:1
1.前16和後16位對齊後異或,那麼這時候原來32位的奇偶性和目前異或出來的16位的結果一致。
2.同理前8位和後8位對齊異或。
3.同理前4位和後4位對齊異或。
4.同理前2位和後2位對齊異或。
5.同理前1位和後1位對齊異或。
最後只須要判斷最後那一位上是1仍是 0便可。
代碼:
int even_ones(unsigned x)
{
unsigned y = x >> 16; x ^= y;
y = x >> 8; x ^= y;
y = x >> 4; x ^= y;
y = x >> 2; x ^= y;
y = x >> 1; x ^= y;
return !(x & 1);
}
問題:用了這個編輯器不能上傳圖片了。。。 [1]: ./images/1.png "1.png"