第一部分
程序結構和執行
Content
信號的表示和處理
現代計算機存儲和處理的信息以二值信號表示。
2.1 信息存儲
最小的可尋址的內存單位是字節(byte)。
2.1.1 十六進制表示法
一個字節由8位組成。
在C語言中,以0x或者0X開頭的的數字常量被認爲是十六進制的值。
tips:
十六進制轉換十進制小技巧:
x = 2 ^n, 當 n 表示成 i + 4 j 的形式,其中0 <= i <= 3,可以把 x 寫成十六進制數開頭數字爲1(i = 0), 2(i = 1), 4(i = 2), 8(i = 3),後面緊跟着 j 個十六進制的 0 。比如:
2048 = 2^11 , n = 11 = i + 4 j = 3 + 4 * 2,後面緊跟着 j 個十六進制的0,從而得到 2048 = 0x800;同理,類似地,512 = 2^9 = 2^(1 + 4 * 2) = 0x200 。
2.1.2 字數據大小
每臺計算機都有一個字長(Word size
),指明指針數據的標稱大小(normal size
)。
ISO C99
引入了一類數據類型,其數據大小是固定的。比如 int32_t
和int64_t
,它們分別爲4個字節和8個字節。
無符號聲明,以下4個聲明意思等效:
unsigned long
unsigned long int
long unsigned
long unsigned int
程序員應該力圖使他們的程序在不同的機器和編譯器上可移植。其中一個方面是使程序對不同數據類型的確切大小不敏感。
2.1.3 尋址和字節順序
#include <stdio.h>
typedef unsigned char *byte_pointer;
void show_bytes(byte_pointer start, size_t len)
{
size_t i;
for(i = 0; i < len; i++)
printf("%.2x", start[i]);
printf("\n");
}
void show_int(int x)
{
show_bytes((byte_pointer)&x, sizeof(int));
}
void show_float(float x)
{
show_bytes((byte_pointer)&x, sizeof(float));
}
void test_show_bytes(int val)
int ival = val;
float fval = (float) ival;
int *pval = &ival;
show_int(ival);
show_float(fval);
show_pointer(pval);
}
int main()
{
test_show_bytes(12345);
return 0;
}
tips:
typedef char *byte_pointer;
等價於
char *start;
2.1.4 表示字符串
C語言中字符串被編碼爲一個以null
(其值爲0)字符結尾的字符數組。
2.1.5 表示代碼
考慮如下C函數:
int sum(int x, int y)
{
return x + y;
}
在示例機器上的代碼如下:
發現其指令編碼是不同的。不同的機器類型使用不同的且不兼容的指令和編碼方式。
2.1.6 布爾代數簡介
二進制是計算機編碼、存儲和操作信息的核心,最簡單的布爾代數是在二元集合{0, 1}
基礎上的定義。
位向量就是固定長度爲 w 、由 0 和 1 組成的串。一個很有用的應用就是表示有限集合。
2.1.7 C語言中的位級運算
C語言其中一個特性是它支持按位布爾運算。
以下是以下對char
數據類型表達式求值的例子:
2.1.8 C語言中的邏輯運算
以下爲一些表達式求值的示例:
2.1.9 C語言中的移位運算
x << k
, 表示 x
左移 k
位,丟棄最高的 k
位,並在右端補 k
個 0,同理右移類似。 移位分有符號移位和無符號移位。
2.2 整數表示
圖2-8爲約定的一些數學術語。
2.2.1 整型數據類型
C語言標準定義了每種數據類型必須能夠表示的最小的取值範圍。如圖2-11
2.2.2 無符號數的編碼
定義:
示例:
2.2.3 補碼編碼
圖2-14展示了針對不同字長,幾個重要數字的位模式和數值。
幾個特性:
補碼範圍不對稱性:|TMin| = |TMax| + 1
最大的無符號數值剛好比補碼的最大值的兩倍大一點:UMax(w) = 2TMax(w) + 1
2.2.4 有符號數和無符號數之間的轉換
C語言運行在各種不同的數字類型之間做強制類型轉換。強制類型轉換的結果保持位值不變,只是改變了解釋這些位的方式。
2.2.5 C語言中的有符號數與無符號數
2.2.6 擴展一個數字的位表示
2.2.7 截斷數字
2.3 整數運算
2.3.1 無符號加法
模數加法形成了一種數學結構,稱爲阿貝爾羣(Abelian group
)。
2.3.2 補碼加法
2.3.3 補碼的非
2.3.4 無符號乘法
2.3.5 補碼乘法
2.3.6 乘以常數
x * 14 = x *( 2^3 + 2^2 + 2^1) = (x << 3) + (x << 2) + (x << 1) = x * (2^4 - 2^1) = ( x << 4) - (x << 1)
2.3.7 除以2的冪
2.4 浮點數
浮點表示對形如 V = x * 2^y
的有理數進行編碼。
2.4.1 二進制小數
十進制表示法使用如下:
二進制表示法如下:
2.4.2 IEEE(I - triple-E)浮點表示
標準浮點格式
浮點格式(C語言中) ` | s |
exp(k) |
frac(n) |
` | |
---|---|---|---|---|---|
單精度(float ) |
1 | 8 | 23 | 32位表示 | |
雙精度(double ) |
1 | 11 | 52 | 64位表示 |
根據exp
的值,被編碼的值可以分爲三種不同的情況
情況1:規格化的值
階碼字段被解釋爲以偏置(biased
)形式表示的有符號整數。即,E = e - Bias
, 其中 e
是無符號數,而 Bias
是一個等於 2^(k-1) - 1
的偏置值。
尾數定義爲 M = 1 + f
。隱含以 1 開頭的(implied leading 1
)表示。
情況2:非規格化的值
階碼值是 E = 1 - Bias, 而尾數的值是 M = f ,不包含隱含的開頭 1 。
情況3:特殊值
2.4.3 數字示例
位 | e | E | 2^E | f | M | 2^E*M | V | 十進制 |
---|---|---|---|---|---|---|---|---|
0 00 00 | 0 | 0 | 2^0 | 0/4 | 0/4 | 0/4 | 0/4 | 0.00 |
0 00 01 | 0 | 0 | 2^0 | 1/4 | 1/4 | 1/4 | 1/4 | 0.25 |
0 00 10 | 0 | 0 | 2^0 | 2/4 | 2/4 | 2/4 | 1/2 | 0.50 |
0 01 00 | 1 | 0 | 2^0 | 0/4 | 4/4 | 4/4 | 1 | 1.00 |
0 01 01 | 1 | 0 | 1 | 1/4 | 5/4 | 5/4 | 5/4 | 1.25 |
0 01 10 | 1 | 0 | 2^0 | 2/4 | 6/4 | 6/4 | 3/2 | 1.50 |
0 01 11 | 1 | 0 | 2^0 | 3/4 | 7/4 | 7/4 | 7/4 | 1.75 |
0 10 00 | 2 | 1 | 2^1 | 0/4 | 4/4 | 4/2 | 2 | 2.00 |
0 10 01 | 2 | 1 | 2^1 | 1/4 | 5/4 | 5/2 | 5/2 | 2.50 |
0 10 10 | 2 | 1 | 2^1 | 2/4 | 6/4 | 6/2 | 3 | 3.00 |
0 10 11 | 2 | 1 | 2^1 | 3/4 | 7/4 | 7/2 | 7/2 | 3.50 |
0 11 00 | - | - | - | - | - | - | - |
2.4.4 舍入(rounding)
示例
2.4.5浮點運算
2.4.6 C語言中的浮點數
2.5 小結
計算機將信息編碼爲位(bite
),通常組織成字節序列。有不同的編碼方式用來表示整數、實數和字符串。不同的計算機模型在編碼數字和多字節數據中的字節順序時使用不同的約定。
C語言的設計可以包含多種不同字長和數字編碼的實現。64位字長的機器逐漸普及,並正在取代統治市場長達30多年的32位機器。由於64位機器也可以運行32爲機器編譯的程序,我們的重點就放在區分32位和64位程序,而不是機器本身。64位程序的優勢是可以突破32位程序具有的4GB地址的限制。
大多數機器對整數使用補碼編碼,而對浮點數使用IEEE標準754編碼。在位級上理解這些編碼,並且理解算術運算的數學特性,對於想使編寫的程序能在全部數值範圍上正確運算的程序員來說,是很重要的。
在相同長度的無符號和有符號整數之間進行強制類型轉換時,大多數C語言實現遵循的原則是底層的位模式不變。在補碼機器上,對於一個 w 位的值,這種行爲是由函數 T2Uw
和 U2Tw
來描述的。C語言隱式的強制類型轉換會出現許多程序員無法預計的結果,常常導致程序錯誤。
由於編碼的長度有限,與傳統整數和實數運算相比,計算機運算具有非常不同的屬性。當超出表示範圍時,郵箱長度能夠引起數值溢出(overflow
)。當浮點數非常接近於 0.0 , 從而轉換成零時,也會下溢。
和大多數其他程序語言一樣,C語言實現的有限整數運算和真實的實數運算相比,有一些特殊的屬性。例如,由於溢出,表達式 x * x
能夠得出負數。但是,無符號和補碼的運算都滿足整數運算的許多其他屬性,包括結合律、交換律和分配律的屬性,還利用了移位和乘以2的冪之間的關係。
我們已經看到了幾種使用位級運算和算術運算組合的聰明方法。例如,使用補碼運算, ~x + 1
等價於-x
。另外一個例子,加速我們想要一個形如[0,……,0, 1,……,1]]
的位模式,有 w - k
個 0 後面緊跟着 k
個 1 組成。這些位模式有助於掩碼運算。這種模式能夠通過C表達式(1 << k) - 1
生成,利用的是這樣一個屬性,即我們想要的位模式的數值爲2^k - 1
。例如, 表達式(1 << 8) - 1
將產生位模式0xFF
。
浮點表示通過將數字編碼爲 x * 2^y
的形式來近似地表示實數。最常見的浮點表示方式是由IEEE
標準754
定義的。它提供了幾種不同的精度,最常見的是單精度(32位)和雙精度(64位)。IEEE浮點也能夠表示特殊值 +無窮
、-無窮
和NaN
。
必須非常小心地使用浮點運算,因爲浮點運算只有有限的範圍和精度,而且並不遵守普遍的算術屬性,比如結合性。