前言
最近突然想起之前去參加的一次面試經(jīng)歷,是一家還算不錯的公司,怎么說呢,因為公司是做工具類軟件的,電腦端網(wǎng)頁端手機端都有,軟件的用戶量達到了3.5億之多,由于面向用戶主要是歐美,因此在國內(nèi)知道的人就很少。我面試的是iOS崗位,不過一開始技術面試一上來問了我一堆計算機基礎底層的問題,其中一個問題就給我留下深刻的記憶,為啥呢?因為他問我int為4個字節(jié)時取值范圍是多少,他聽到我答案后,斬釘截鐵的說我肯定錯了,但是呢,這個算是屬于計算機的常識問題吧,我從大學到現(xiàn)在六七年,一直理解的是這個答案,這次面試卻有人態(tài)度異常堅決的說我肯定錯了。于是,我面試完后回家后就好好的在網(wǎng)上和書上找了答案,果然,我是對的,他是錯的。這個給我感觸很大,讓我感覺到,現(xiàn)在工作中,應該還有很多他這樣的人,對一個知識的認知是錯的,并且還是那么的堅定。因此,我認為,計算機的知識一定要知其所以然,不然真的很可能堅持一個錯誤的認知好多年。
正文
如果您現(xiàn)在覺得這個問題的答案不是這個的話,那么您就很有必要好好的看下我接下來寫的內(nèi)容了。如果您現(xiàn)在知道是這個答案,但是不知道為啥會是這個值的話,也可以好好看看我接下來寫的。如果你已經(jīng)知道的原因,其實你也可以看看,說不定能對你有更深層次的理解。
解釋
剛說的4字節(jié)的int默認是在C/C++語言中的,并且我們默認說也是有符號的int,也即是包括負數(shù)的。
解釋這個問題之前,我得先給大家說明一個小知識。眾所周知,一個字節(jié)等于8位(1 byte == 8 bit),為何會有位這個東西呢,原因很簡單,這是由于如今大家用到的計算機CPU以及所有集成電路,都是只能識別電信號的,在這些計算機硬件的眼中,電信號可分為兩種,一種是高電平,一種是低電平,高電平表示1,低電平表示0,這就是為啥計算機會只能識別0、1這種二進制數(shù)的原因了(當然量子計算機除外)。在CPU中,接收高低電平的硬件叫做管腳,就例如我們常說的內(nèi)存條金手指,一根管腳能接收一個0、1,由于CPU內(nèi)部的設計,每多一根管腳,能表示的數(shù)值是以指數(shù)增長的,也就是如果是16根管腳的話,那么CPU的尋址能力就是2^16,同理,32根就是2^32。說到這兒,大家應該也終于是知道了我們常說的位數(shù)是啥玩意了吧,沒錯,就是CPU的管腳數(shù)(當然這個僅僅是CPU的位數(shù),操作系統(tǒng)同樣也是有位數(shù)的,當一臺電腦的CPU和系統(tǒng)都是64位的是才能真正完全發(fā)揮出64位的功效)。
無符號
這里我們得先好好的說說無符號類型,當一個無符號類型的8位,也就是一個字節(jié)時,能表示的數(shù)值個數(shù)是多少?是滴,就是2^8,不過呢,我也可以好好的解釋下這個,當為一位的時候,能表示的數(shù)值是0和1,也就是2^1,當為2位時,能表示的數(shù)值是0、1、2和3,也就是2^2,以此類推,8位能表示的數(shù)值個數(shù)就是2^8,相信你對這個不會有什么異議吧。再來,8位能表示的最大值是多少?? 答案是,2^8 - 1 ,也就是255,這個同樣可以解釋下,當為一位時,最大值的二進制就是1,也就是十進制的1,也就是2^1 - 1,當為2位時,最大值的二進制是11,也就是十進制的3,也就是2^2 - 1,同理,8位時,最大值的二進制是8個1,也就是1111 1111,算下來的十進制數(shù)值就是255,也就是2^8 - 1。8位能表示的最小值想必都知道,就是八個0,也就是0。
這樣子算下來,剛好,從0到2^8 - 1,每一個數(shù)值唯一的對應著相應的一個二進制值表示。這里的意思就是,0剛好唯一對應著0000 0000這個二進制數(shù),255剛好唯一對應著1111 1111這個二進制數(shù)。這樣子,每個數(shù)值都唯一對應著一個二進制數(shù)表示,所以每個數(shù)值都可以很好很和諧的存儲著。
有符號
有了上面對無符號的解釋,接下來才能好好的給你解釋有符號的情況。
所謂的有符號,意思就是,有負數(shù)。為啥有負數(shù)時,會說成是有符號呢?那我就問你一個問題,按照剛剛上面對于無符號的解釋后,你覺得同樣是一個8位的數(shù)值,同樣是每個位只能識別0、1的CPU,他到底要怎樣才能知道這個二進制數(shù)表示的是正數(shù)還是負數(shù)呢?前人們也想到這個問題了,為了解決這個問題,就引出了一條規(guī)定,當有負數(shù)存在的時候,將最高位作為標記位,當標記位為1時,就代表這個數(shù)值是個負數(shù),當標記位為0時,就是正數(shù)。這個標記的數(shù)值剛好決定了是正數(shù)"+"這個符號,還是負數(shù)"-"這個符號,這就是有符號出現(xiàn)的原因。
為何8位取值范圍是-2^7 到2^7- 1
首先我來給大家說一個錯誤的答案解釋。大家現(xiàn)在已經(jīng)知道了,當有負數(shù)的時候,最高位是符號位,也就是說,現(xiàn)在這八位里能真正拿來存儲值的位數(shù)只剩七位,也就是最大值是2^7 - 1再加上符號位的話,也就是,-2^7 - 1到2^7 - 1。大家看到這里是不是感覺很有道理?。渴遣皇遣⒏杏X不到有錯呢?如果你感覺這個很合理的話,你現(xiàn)在的理解就是跟面試我的那個人的想法差不多了,這可能也是為啥他到現(xiàn)在還那么堅定的認為自己是對的的原因吧。
其實呢,出現(xiàn)這樣一個錯誤的認識,最主要還是歸結于自己沒認真去想過這個問題。現(xiàn)在我就給大家說下,這樣的理解存在一個很致命的問題,那就是,0的表示,你會發(fā)現(xiàn),當負0時,對應的二進制數(shù)值是1000 0000,當正0時,對應的二進制數(shù)值是0000 0000,不知道看到這兒,你有沒看出端倪,無論看沒看粗來,我都得說粗來,那就是,本來數(shù)學上,正0和負0表示的是同一個數(shù),然鵝在這種情況下,正0和負0表示了兩個數(shù)值,也就是計算機內(nèi)部用了兩個二進制數(shù)來表示和存儲這兩個0,現(xiàn)在是不是覺得恍然大悟呢?
原碼和補碼
前人大神們?yōu)榱私鉀Q這個問題,就想出了一個絕妙的方法,那就是補碼。說到補碼的話,就不得不提到的是原碼和反碼,為啥要說這兩個東西呢,因為補碼是通過原碼和反碼算出來的。前人們規(guī)定,原碼就是那個數(shù)值直接算出來的二進制數(shù),例如,1的原碼就是,0000 0001,-1的原碼就是,1000 0001。反碼呢,就是,除符號位外,其他所有位取反,例如,-1的反碼就是,1111 1110。我這里為啥不提1的反碼呢,因為還有一條規(guī)定就是,正數(shù)的反碼和補碼都是他的原碼,為啥這么規(guī)定呢,其實想想都知道原因很簡單,那就是,本來正數(shù)的二進制數(shù)表示都是一一對應的,因此就沒必要再大費周章了,當然,如果不這么規(guī)定的話,最終補碼的形式也是不會一一對應一個二進制數(shù)的。
接下來就是補碼了,反碼算出來了,補碼就很簡單了,那就是反碼加1,例如,-1的補碼就是,1111 1110 + 1 = 1111 1111。這里提個注意點,就是,在計算補碼的時候,最高位也是會參與計算的,也就是說,如果反碼是1111 1111的話,補碼 = 1111 1111 + 1 = 1 0000 0000,這里最高位1已經(jīng)超出了八位,也就是常說的溢出了,那么就直接忽略了,也就是最終結果是0000 0000,大家猜猜這個補碼的原碼是誰?沒錯,就是負0,之后再看看正0,我們說過正數(shù)的原碼、反碼和補碼都是原碼,也就是0000 0000,看見沒,這種情況下,正負0都是同一個二進制表示。這就已經(jīng)很好的解決了0的問題。
沖突解決
這里就開始好好的跟大家說說這個取值范圍。
首先,先說一一對應的事,大家已經(jīng)知道了,當有負數(shù)存在的情況下,最高位是符號位,之后再根據(jù)原碼、反碼和補碼的一系列規(guī)定和計算,有一點是可以確定的,那就是,負數(shù)和正數(shù)的二進制表示絕對不可能沖突,意思就是不會存在一個負數(shù)的二進制表示和某個正數(shù)的二進制表示是一樣的,就是因為,負數(shù)補碼的最高位永遠是1,正數(shù)補碼的最高位永遠是0,能讓負數(shù)補碼最高位為0的情況,只有一種,那就是0的時候。為0時剛好就解決了正負數(shù)存儲時正負0二進制表示不一致的問題,這也是補碼的一個作用之一。之后就是正數(shù)的一一對應,之前也說過,正數(shù)情況下,補碼就是原碼,所以正數(shù)是肯定不可能存在正數(shù)之間的數(shù)值沖突的。最后再說負數(shù),由于,原碼時,每個二進制都是一一對應的(跟正數(shù)同理),那么負數(shù)的所有反碼也都是肯定一一對應的,如果大家理解不了,可以自己試試用二進制看看,你會發(fā)現(xiàn),無論是原碼還是反碼,每個數(shù)值的表示,肯定至少有一位跟其它任何數(shù)值都不一樣,這就證明的唯一性。既然原碼、反碼都具有唯一性了,那么再加上一個1的話,仍然具有唯一性。
有符號8位的取值范圍
費勁千辛萬苦,終于來到了這里。首先,我們這里再回顧一下,就是,當存在符號位時,8位能表示的最大值就是111 1111,也就是7個1,也就是2^7 -1, 所以正數(shù)的范圍就是0到2^7 - 1,負數(shù)就是-(2^7 - 1)? 到-1,但是,這樣算下來的話,總共表示的數(shù)值個數(shù)是2^7 + 2^7 - 1 = 2^8 -1,這可是比8位能存儲的2^8這個數(shù)值少一個呢,這樣不就活生生的浪費了一個麼?不知大伙有沒注意到一個情況,那就是當為負數(shù)時,補碼是1000 0000時,我們通過這個補碼反向算得反碼是0111 1111,原碼就是0000 0000是不是感覺很詭異,這不是正0嗎?言下之意就是說,補碼1000 0000這個二進制位壓根不可能有,這就是剛說的存儲二進制位中少的那個。但是呢,由于1000 0000本身代表的是128,再加上最高位為1,那么就是個負數(shù),再加上所有的二進制表示又少了一個,因此,1000 0000就順理成章的成了-128,當然,1000 0000是補碼,它沒有原碼和反碼。最后再加上一個計算機內(nèi)部對負數(shù)的運算方式吧,就是對負數(shù)整體取絕對值,之后取反加1,算下來就是:-128(取絕對值) -> 128(變成二進制表示) ->1000 0000(取反)->0111 1111(加1) -> 1000 0000(補碼)。-127(取絕對值)->127(變成二進制表示)->0111 1111(取反)->1000 0000(加1)->1000 0001(補碼)。-126(取絕對值)->126(變成二進制表示)->0111 1110(取反)->1000 0001(加1)->1000 0010(補碼)。你會發(fā)現(xiàn),計算機內(nèi)部對負數(shù)補碼的運算的結果和我們之前說的運算結果是一毛一樣的,從這里也就能清楚的看到,-128通過計算機內(nèi)部運算之后的補碼就是1000 0000
為何4個字節(jié)int取值范圍是-2^31 到2^31 - 1
這里大伙應該就能清楚明白的知道為何4個字節(jié)int取值范圍是-2^31 到2^31 - 1了吧。