上一篇博客咱們講解了計算機中整數的表示,包括無符號編碼和補碼編碼,以及它們之間的互相轉換,我的以爲那是很是重要的知識要點。這篇博客咱們將介紹C語言中的有符號數和無符號數以及擴展和截斷數字。html
上一篇博客咱們給出了C語言中在32位機器和64位機器中支持的整型類型數據,咱們這裏只給出32位機器上的:java
儘管 C 語言標準沒有指定有符號數要採用某種編碼表示,可是幾乎全部的機器都使用補碼。一般大多數數字是默認有符號的,好比當聲明一個像12345或者0xABC這樣的常量的時候,這個值就被認爲是有符號的。編碼
C 語言容許有符號數和無符號數之間的轉換。在一臺採用補碼的機器上:spa
①、無符號數轉換成有符號數3d
②、有符號數轉換成無符號數htm
咱們能夠看下面這個程序:blog
#include <stdio.h> int main() { char t = 0xFF; //%d把對應的整數按有符號十進制輸出,%u把對應的整數按無符號十進制輸出 //有符號的轉換成無符號的 printf("t=%d,t2u=%u\n",t,(unsigned char)t); unsigned char u = 0xFF; //%d無符號轉換成有符號的 printf("u=%u,u2t=%d\n",u,(char)u); return 0; }
結果爲:get
爲何是這個結果,我在上一篇博客:深刻理解計算機系統(2.4)------整數的表示(無符號編碼和補碼編碼)已經講過了,這就是數據類型的強制轉換。博客
還有第二種狀況是當一種類型的表達式被賦值給另外一種類型的變量時,轉換是隱式的。好比:io
#include <stdio.h> int main() { unsigned char u = 0xFF; char t = u; //%d無符號轉換成有符號的是默認的 printf("u=%u,u2t=%d\n",u,t); return 0; }
結果是:
咱們將一個無符號的數賦值給有符號的,其轉換是隱式的發生的。這對於標準的運算來講並沒有差別,可是對於像 < 和 > 這樣的關係運算來講,會致使錯誤的結果。
注意:在 C 語言中,當執行一個運算,會隱式的將有符號參數強轉爲無符號參數。
#include <stdio.h> int main() { printf("%d\n",-1<0u);//結果是0,0表示錯誤 1表示正確 printf("%d\n",-123<123u);//0 return 0; }
咱們解釋第一個 -1 < 0u 爲何是錯誤的。由於0u是無符號的,-1是有符號的。那麼-1就會被轉換成無符號的。
也就是T2Uw(-1)=-1+232=4 294 967 296-1=4 294 967 295
那麼 -1 < 0u 表達式也就變成了 4 294 967 295u < 0u ,結果固然是錯誤的。第二個例子咱們也能夠這樣分析,這裏就不詳細描述了。
因此咱們要注意實際編碼過程當中因爲隱式轉換所形成的錯誤運算。
擴展一個數字的位,簡單來講就是在不一樣字長的整數之間轉換,而這種轉換咱們能夠須要保持先後數值不變。固然將一個數據轉換爲字長更小的數據類型的時候,它的值確定會發生變化。那麼咱們只能將較小的數據類型轉換爲較大的數據類型。好比將短整型short int 轉換爲整型 int。,那該怎麼辦呢?
①、零擴展
將一個無符號數轉換爲一個更大的數據類型,咱們只須要簡單的在二進制序列前面添加 0 便可。
②、符號位擴展
將一個補碼數字轉換爲一個更大的數據類型,咱們須要在開頭添加符號位。
由上面兩條咱們能夠總結:若是咱們原始位爲[xw-1 , xw-2 , … , x2 , x1 , x0],那麼擴展後就能夠表示爲:[xw-1 ,xw-1 ,...,xw-1 , xw-2 , … , x2 , x1 , x0]。
即咱們想證實:
在表達式的左邊,咱們增長了 k 位的xw-1副本。若是咱們證實符號位擴展一位,即 k=1,而值是保持不變的。那麼對於任意的k都能保持這種屬性。那麼等式變爲:
因爲無符號的,添加0,這很好理解,先後數值不變。那麼咱們證實有符號的補碼編碼:
因爲:
將上面的補碼編碼替換等式右邊,即:
上面的證實咱們只須要知道:2w-2w-1=2w-1 即很好理解了。
這和上面的擴展恰好相反。即咱們不須要額外的擴展一個數的位,而是減小一個數字的位數。
將一個 w 位的數 [xw-1 , xw-2 , … , x2 , x1 , x0] 截斷爲一個 k 位數字時,咱們會丟棄高 w-k 位。獲得 [xk-1 , xk-2 , … , x2 , x1 , x0]
對於無符號截斷公式爲:
證實過程以下:
而對於有符號(補碼編碼)的截斷,咱們只須要多加一步,將無符號編碼轉換爲補碼編碼就能夠了。
好比下面這個程序:
#include <stdio.h> int main() { int i = 53191; short int j = (short)i; int k = j; printf("%d %d %d\n",i,j,k); return 0; }
結果爲:
咱們將 i 強轉爲 short int,在 64位機器上,就是將 32 位的 int 截斷爲 16 位的short int,這個16位的位模式就是 -12345 的補碼錶示。當咱們把它強轉爲 int 時,符號位擴展把高 16 位設置爲 1,從而生成 -12345 的32 位補碼錶示。
本篇博客講解了 C 語言中的有符號數和無符號數,以及擴展和截斷一個數值是如何進行的,理解它們的原理是十分必要的。
咱們從上面已經看到了許多無符號運算的特殊性,尤爲是有符號數到無符號數的隱式轉換會致使錯誤。而避免這類錯誤的方法是不使用無符號數。實際上,除了 C 語言,不多有語言支持無符號數。好比 Java只支持整型數據,而且要求補碼運算。
那麼計算機中整數的表示就已經講完了,下篇博客將會講解計算機中整數的運算,咱們出現的兩個數運算會產生莫名其妙的結果在下一篇博客會獲得解答。