1. 程式人生 > >C語言再學習 -- 負數

C語言再學習 -- 負數

有符號數的表示方法是由硬體決定,而不是由C決定的。有三種表示方法:

1、二進位制原碼

0000 0001  表示 1

1000 0001  表示 -1

這個方法有個缺點是有兩個零: +0 和 -0。這會引起混淆,而且用兩個位組合來表示一個值也有些浪費。


2、二進位制補碼(最普遍的系統)

區別在於 singned 和 unsigned:

1)如果是無符號位元組, 1000 0000 該組合為 128

2)如果是有符號位元組, 1000 0000 該組合為 -128

第一種表示數的範圍是 0 ~ 255

第二種表示數的範圍是 -128 ~ +127,對於一個二進位制補碼數取負數,最簡單的方法就是取反、加 1


3、二進位制反碼

通過反轉位組合中的每一位以形成一個數的負數,例如:

0000 0001  表示 1

1111 1110  表示 -1

這種方式也有一個 -0:1111 1111。其範圍是 -127 ~ +127


下面重點介紹二進位制補碼:

計算機中的負數是以其補碼形式存在的 補碼=原碼取反+1

一個位元組有8位 可以表示的數值範圍在 -128到+127
用二進位制表示也就是 10000000 - 01111111(注意:最高位表示符號)
最高位是1的都是負數 最高位是0的都是正數

二進位制數是逢二進一 只有0和1兩個數字 沒有2
如-7 原碼是 10000111 然後取反(最高位是符合不用取反)得11111000
加一 得11111001 那麼-7的二進位制數就是 11111001
再如 -10 原碼是 10001010 取反得 11110101 加一得 11110110

那麼-10的二進位制數就是 11110110


下面就考慮一下這個問題:


  
  1. #include <stdio.h>
  2. int main()
  3. {
  4. char a[ 1000];
  5. int
    i;
  6. for(i= 0; i< 1000; i++)
  7. {
  8. a[i] = -1-i;
  9. }
  10. printf( "%d", strlen(a));
  11. return 0;
  12. }
  13. 輸出結果:
  14. 255


按照負數補碼的規則,可以知道-1的補碼為0xff, -2的補碼為0xfe……當 i 的值為 127時, a[127]的值為-128,而-128 是 char 型別資料能表示的最小的負數。當 i 繼續增加, a[128]的值肯定不能是-129。因為這時候發生了溢位, -129 需要 9 位才能儲存下來,而 char 型別資料只有 8 位,所以最高位被丟棄。剩下的 8 位是原來 9 位補碼的低 8 位的值,即 0x7f。當 i 繼續增加到 255 的時候, -256 的補碼的低 8 位為 0。然後當 i 增加到 256 時, -257 的補碼的低 8 位全為 1,即低八位的補碼為 0xff,如此又開始一輪新的迴圈……

按照上面的分析, a[0]到 a[254]裡面的值都不為 0,而 a[255]的值為 0。 strlen 函式是計算字串長度的,並不包含字串最後的‘ \0’。而判斷一個字串是否結束的標誌就是看是否遇到‘ \0’。如果遇到‘ \0’,則認為本字串結束。

所以輸出結果為 255 


最後思考一個問題:-0 和+0 在記憶體裡面分別怎麼儲存?

以char型別為例
對於正數原碼、反碼以及補碼是其本身
+0
原碼 00000000
反碼 00000000
補碼 00000000

負數的原碼是其本身,反碼是對原碼除符號位之外的各位取反,補碼則是反碼加1
-0
原碼 10000000
反碼 11111111
補碼 1 00000000

因為char型別只有8位,所以放棄最高位。由此可見,在計算機儲存系統中+0 及 -0 的補碼是一致的即採用補碼的方法,可以將+0 及 -0 統一表示,否則需要將+0 和-0 區別對待,增加運算複雜度。

上面是以char為例,換成short和int等,原理是一樣的。可以親自動手加深印象。