為什麼0取反再右移任意位得出數總是-1?
對有符號的整數型別(如 int)進行右移,在標準中是由平臺自行定義的,不過一般是使用二補數表示有符號整數,而右移是算術右移(arithmetic right shift),即是會用原來的最高位(MSB)來填補右移後左面的空位。
字面量 0 的型別為 int,取反後全部位為 1,作為有符號整數是 -1,算術右移後也不變。
如果用字面量 0u,結果就不一樣了。
圖源:
https://
en。m。wikipedia。org/wiki
/Arithmetic_shift#/media/File%3ARotate_right_arithmetically。svg
最近正好在看CSAPP(Computer System: A Programmer‘s Perspective,中文譯本為深入理解計算機系統),就來回答一下叭w~
首先簡單介紹一下機器中整數(int)的儲存形式:絕大多數情況下,不管是32位機器還是64位機器,int都是4個位元組(32位)。其中最高位(MSB, the Most Significant Bit)
視作符號位,符號位為0則為非負數,符號位為1則為負數。
用向量的形式表示計算機中字元的儲存,則可以表示為
,對應表示的整數為
。所以,int可以表示的最大數
,最小數
。
0作為整數(Integer),在機器中的表示形式為000。。。。000(32個0),取反後為1111。。。1111。根據上述計算公式可得二進位制碼1111。。。1111即代表整數-1。
那麼為什麼右移任意位依然是-1呢?
右移有兩種形式:邏輯右移(logical right shift)和算數右移(arithmetic right shift)。
邏輯右移是將左邊空位補0。所以將int右移k位便得到
。
算數右移是將左邊空位補上MSB位。所以將int右移k位便得到
。通俗的講法即非負數右移補0,負數右移補1。
C標準並沒有規定應該使用哪一種右移方式,不過絕大部分情況下使用的是算數右移。而1111。。。1111右移任意位依然是1111。。。1111,所以結果總是-1。
沒記錯的話C語言中右移一個負數,左邊補0還是1是implementation defined,不過就算從算數角度說,0取反右移也是1啊:
不考慮實際條件的限制,0等於是……0000000000(無窮個0),取反後是……111111111111(無窮個1),無窮個1右移還是無窮個1,也就是-1嘛
為啥無窮個1是-1呢,計算0-1,不夠減,往前借位,還不夠借,無限借下去,想象一下最後“假裝”借到了,就變成無窮1了,或者無窮個1加一,無限進位過去,就是0了
注:差不多是這個道理,但上面的某些“證明”別當真,哈哈
你如果是按位取反,~0就變成了111111。。。1,右移一般補符號位,也就是還是111。。。1,也就是補碼的-1了。
如果你是邏輯取反,!0就是1,右移就是0了。
那得看型別吧,signed型別是算數右移,補最高位,unsigned是邏輯右移補0