有符號和無符號的區別
最近總有同事說我的髮際線上升了,我就告訴他,其實並不是我的髮際線在後退,而是我在前進
https://www。zhihu。com/video/1446823025617973248
一、原碼
反碼
補碼
從不同的角度去看情況,往往會得到不同的結果,在前面的課程我們舉得例子都是正數,沒有看到負數,難道計算機沒有負數一說?當然不是,在講有符號 和 無符號的時候 我們需要了解一下計算機是怎麼儲存資料。
1.1
原碼 補碼 反碼
計算的二進位制資料有3種表現形式分別為:原碼 補碼 反碼。
首先我們看到原碼是可以理解的,例如:以1個位元組計算,10的原碼是:0000 1010
那為什麼會有補碼和反碼的存在呢?這是由於CPU的關係,等會我們再細緻講解。
我們的計算機在記憶體中其實存放二進位制資料的形式是補碼。
之前的課程中我們講到的例子全是正數沒有用到過負數。
其實計算機的型別會分為有符號型別(正/負號)和無符號型別(正號沒有負號),一般我們的正號是可以省略的。
它們均可以在型別名前加“unsigned”關鍵字表示為無符號,預設不加這個關鍵字就是有符號型別,
1.2
符號位和數值位
原碼 補碼 反碼三種形式均有符號位和數值位兩部分,
1.2.1
符號位:
符號位 0 表示正數,符號位 1 表示負數。
1.2.2
數值位:
三種表示形式各不相同,這樣也是有原因的,因為使用補碼,可以將符號位和數值域統一處理。
加法和減法也可以統一處理(因為CPU只有加法器,而沒有減法器)。
補碼與原碼相互轉換,其運算過程是相同的,不需要額外的硬體電路。
1.2.3
原碼怎麼計算得到補碼呢?
原碼的絕對值
1。直接將二進位制按照正負數的形式得到原碼後取絕對值後翻譯成二進位制就可以了。
反碼
2。位依次按位取反就可以得到了。
補碼
3。反碼 +1 就得到補碼。
注意:正數的原碼、反碼、補碼相同。
1.2.4
例如:
unsigned char a = 198;char b = -58;printf(“%d\r\n”,b);//記憶體中存198return 1;
最高位1表示負數,數值位表示值
1。原碼取絕對值:1011 1010 (原碼) => -58 => -011 1010 =>|58| => |-011 1010| => 0011 1010
2。按位取反:0011 1010 => 1100 0101
3。反碼+1遵循逢二進一:1100 0110 +1 => 0xC6
這就得到了我們變數b在記憶體中存的補碼值為0xC6。
所以這就解釋了為什麼我們看變數a,b的記憶體值都是0xC6。
1.2.5
有符號無符號總結:
1。符號位 0 表示正數,符號位 1 表示負數。
2。正數的原碼就等於補碼。
3。負數的原碼不等於補碼。
4。補碼等於原碼取絕對值轉換成二進位制然後取反再加1得到的。
5。所以僅僅從記憶體看值 是無法確定是有符號還是無符號的,計算機是識別不了的,需要編譯器操作一系列指令來識別。
1.2.6
為什麼用補碼儲存呢?
有的人會問用原碼多好還能直接看出來
其實並不然,計算機有時候還並不是那麼智慧的
CPU只會加法並不會減法所以才是補碼儲存也就是說CPU只有加法器並沒有減法器
我們來看看正數的補碼 和 負數的補碼 相加是不是得到了正確的結果呢?
1.2.7.例如:
10+(-2)=8? 看看計算機的計算流程
正數10的補碼等於原碼:
0000 1010
負數-2的補碼不等於原碼:
這裡要計算出補碼
1000 0010 取絕對值
0000 0010 取反
1111 1101 +1得到補碼
1111 1110 計算機驗證下就是等於-2
由於CPU只有加法器
我們就把2個補碼相加
0000 1010
1111 1110
逢二進一等於
0000 1000
驗證下是不是等於8?
計算是對的
所以明白了嗎??
二、有符號
無符號
1。經過上面的講解 我們在來理解一下 有符號 和 無符號
2。觀察下面程式碼 並編譯 顯示
3。我們觀察記憶體中 這2個變數的值
4。發現一樣 !!再回憶下上面我們講到的負數的補碼 是不是就迎刃而解了
5。頓時恍然大悟的感覺
#include
我們這裡面發現 a和b 的輸出是不同的
我們到記憶體中看看情況
發現a和b 在記憶體中是相同的,都是C6
是否有符號不會影響他在記憶體中的存放方式呢?相信經過上面的講解大家也知道怎麼回事了。
因為記憶體中只有0和1 表示不了正負的 都是人為規定的
無符號範圍 0 —— 255
有符號範圍 0 ——127 和 -128—— -1
為什麼這樣 ? 人為規定的!
經過上面講解原碼 反碼 補碼的時候知道了為什麼都是C6的原因。
這裡我們還需要知道無符號的198是怎麼轉為有符號-58的。
其實很簡單,我們只需要, -128 +(198-128) = -58 。
這裡給大家畫了一張圖 以便大家去理解這個公式的由來:
習題+考核
//當負數超過最小負數的時候
unsigned char a = 58;
char b = -198;
printf(“%d\r\n”,b);//記憶體中存58
return 1;
最高位1又表示負數 又是真實值 因為198最高位必為1
11000110 原碼-|1100 0110 |-198|=|198|=|-/1100 0110| 取絕對值 |1100 0110|
00111001+1 反碼+1 |1100 0110| 取反 |0011 1001|
00111010 為58 |0011 1001| +1 得到補碼00111010