深入理解電腦系統(2.5)------C語言中的有符號數和無符號數以及擴充和截斷數字

來源:互聯網
上載者:User

標籤:情況   str   資料類型   符號   數值   指定   clu   系統   class   

上一篇部落格我們講解了電腦中整數的表示,包括無符號編碼和補碼編碼,以及它們之間的互相轉換,個人覺得那是非常重要的知識要點。這篇部落格我們將介紹C語言中的有符號數和無符號數以及擴充和截斷數字。

 

1、C語言中的有符號數和無符號數

  上一篇部落格我們給出了C語言中在32位機器和64位機器中支援的整數型別資料,我們這裡只給出32位機器上的:

  

  儘管 C 語言標準沒有指定有符號數要採用某種編碼錶示,但是幾乎所有的機器都使用補碼。通常大多數數字是預設有符號的,比如當聲明一個像12345或者0xABC這樣的常量的時候,這個值就被認為是有符號的。

  C 語言允許有符號數和無符號數之間的轉換。在一台採用補碼的機器上:

  ①、無符號數轉換成有符號數

    

 

  ②、有符號數轉換成無符號數

    

  我們可以看下面這個程式:

1234567891011121314 #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;}

  結果為:

  

  為什麼是這個結果,我在上一篇部落格:深入理解電腦系統(2.4)------整數的表示(無符號編碼和補碼編碼)已經講過了,這就是資料類型的強制轉換

  還有第二種情況是當一種類型的運算式被賦值給另一種類型的變數時,轉換是隱式的。比如:

12345678910 #include <stdio.h> int main(){    unsigned char u = 0xFF;    char t = u;    //%d無符號轉換成有符號的是預設的     printf("u=%u,u2t=%d\n",u,t);    return 0;}

  結果是:

  

  我們將一個無符號的數賦值給有符號的,其轉換是隱式的發生的。這對於標準的運算來說並無差異,但是對於像 < 和 > 這樣的關係運算來說,會導致錯誤的結果。

  注意:在 C 語言中,當執行一個運算,會隱式的將有符號參數強轉為無符號參數。 

12345678 #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 ,結果當然是錯誤的。第二個例子我們也可以這樣分析,這裡就不詳細描述了。

  所以我們要注意實際編碼過程中由於隱式轉換所造成的錯誤運算。

 

2、擴充一個數位位表示

  擴充一個數位位,簡單來說就是在不同字長的整數之間轉換,而這種轉換我們可以需要保持前後數值不變。當然將一個資料轉換為字長更小的資料類型的時候,它的值肯定會發生變化。那麼我們只能將較小的資料類型轉換為較大的資料類型。比如將短整型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 即很好理解了。

 

 

3、截斷數字

  這和上面的擴充剛好相反。即我們不需要額外的擴充一個數的位,而是減少一個數位位元。

  將一個 w 位的數 [xw-1 , xw-2 , … , x2 , x1 , x0] 截斷為一個 k 位元字時,我們會丟棄高 w-k 位。得到 [xk-1 , xk-2 , … , x2 , x1 , x0]

  對於無符號截斷公式為:

  

  證明過程如下:

    

 

   而對於有符號(補碼編碼)的截斷,我們只需要多加一步,將無符號編碼轉換為補碼編碼就可以了。

    

  比如下面這個程式:

12345678910 #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 位補碼錶示。

 

4、總結

  本篇部落格講解了 C 語言中的有符號數和無符號數,以及擴充和截斷一個數值是如何進行的,理解它們的原理是十分必要的。

  我們從上面已經看到了許多無符號運算的特殊性,尤其是有符號數到無符號數的隱式轉換會導致錯誤。而避免這類錯誤的方法是不使用無符號數。實際上,除了 C 語言,很少有語言支援無符號數。比如 Java只支援整型資料,並且要求補碼運算。

  那麼電腦中整數的表示就已經講完了,下篇部落格將會講解電腦中整數的運算,我們出現的兩個數運算會產生莫名其妙的結果在下一篇部落格會得到解答。

 

深入理解電腦系統(2.5)------C語言中的有符號數和無符號數以及擴充和截斷數字

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.