字符集歷史和亂碼問題(一)

網(wǎng)站亂碼問題我們會經(jīng)常碰到,大多見于非英文的中文字符或其他字符亂碼,而且,這類問題常常是因為編碼方式問題,主要原因就是所保存的編碼方式和瀏覽器解析時用的編碼方式不一致導致。解決辦法就是要知道自己是用什么編碼方式保存的,并告訴瀏覽器用你指定的編碼方式去解析(在html<head>
里添加<meta charset="編碼方式">),否則,瀏覽器就會用默認的編碼方式解析,如果兩者不一致則會亂碼。

以上一段話,實際已經(jīng)可以解決標題的問題了。但字符編碼(常見有:ASCII、GB2312、GBK、ISOLatin-1、Unicode、UTF-8)是計算機存儲技術的基礎知識,我覺得還是有必要了解更多一些,比如:為什么計算機非要是二進制而不是其他進制呢?二進制是怎么產(chǎn)生的?計算機是怎么通過二進制存儲和讀取的?我們電腦上的內(nèi)存和硬盤有何種區(qū)別?32位和64位操作系統(tǒng)指的又是什么?CPU的各種性能指標和二進制的關系?編程又和這些有何關系?等等問題,于是在開啟了搜索模式后,便有了下面這個很長很長的故事:

字符編碼涉及歷史久遠,專業(yè)術語多,又是計算機的基礎,為了讓自己對“字符編碼”又更加深入的認識,以下內(nèi)容參考和閱讀了大量的文章資料,為了讓大家讀上去更有趣,有些內(nèi)容在自己消化后稍有修改,文章太多,無法一一聯(lián)系授權,侵刪。(大概來自于百科、文庫、知乎、博客園、CSDN、維基百科、谷歌、饑人谷講師若愚直播課、阮一峰博客等),文內(nèi)難免有理解錯誤之處,請多指正。

關于進位制的發(fā)現(xiàn)

我們都知道現(xiàn)在人類是離不開數(shù)學運算的,我們古人呢?他們也需要計數(shù)啊,比如我打的獵物要怎么樣分配給我的愛妃們?我們現(xiàn)在自然很容易理解進位制的(十進制、二進制、七進制、八進制、十二進制、十六進制,二十進制、六十進制)運算,但那個時候我們的古人可不懂啊,人類在很久很久以前,對于10以上的數(shù)字是沒有概念的。大家都是就著自己的手指頭數(shù)數(shù),數(shù)到10就達到了極限。后來有個基因突變的天才發(fā)明了一種做法(可能他發(fā)現(xiàn)了復雜的數(shù)字都是由簡單的數(shù)字多次相加得來的道理),讓人類的數(shù)數(shù)能力獲得了質(zhì)的飛升。他是這么做的:數(shù)完了自己的手指頭,就在自己的身前放一顆小石子(人類的邏輯思維是需要不斷進化的,當時可能還記不?。?。然后讓自己的手指頭“清零”,繼續(xù)數(shù)數(shù)。數(shù)完所有東西后,再回來數(shù)數(shù)小石子。于是,人類發(fā)明了二位數(shù)。二位數(shù)發(fā)明之后,三位數(shù)發(fā)明也不遠了,后來不僅出現(xiàn)了十進制(手指),還出現(xiàn)了二十進制(手指+腳指),再后來發(fā)現(xiàn)了二進制、七進制、八進制、十六進制、甚至還有六十進制。十進制是我們現(xiàn)在最能理解和接受的,我們看到100立刻能明白它的意思,但如果看到二進制1100100肯定是懵逼的。所以,二進制雖然最簡單,只需要兩個基本數(shù)值0和1,但使用使用起來卻很麻煩,假如你打獵收獲3個獵物,就得進位,而進位的計算本來就麻煩,故而,瑪雅人寧可使用二十進制數(shù)完手指再數(shù)腳指也不愿進位。再后來,古人有了月份和地支的概念后,十二進制得以使用(還有個說法是人類除大拇指外的其余四根手指都可彎曲為三節(jié),三四得十二)。再再后來,有了十六進制,這個主要用在計算重量單位,比如十六兩合一斤。二十進制據(jù)說是瑪雅人在用,與使用十進制的人群如華夏人相比,瑪雅人在數(shù)二十以內(nèi)的獵物時不需要進位,數(shù)完手指再數(shù)腳指就可以了,這可能會產(chǎn)生一種思維惰性;華夏人數(shù)到十一個獵物就得進位,迫使向更復雜的進位運算前進,這就使一個種族在數(shù)學水平上先進了一步。六十進制主要源于中國十天干與十二地支的組合,也僅用于干支紀年。西方用于分割時間上的小時和分鐘。七進制東西方都在使用,西方把七天作為一個周期,周而復始地循環(huán);中國則把一個月分為四個星期,大月30天則有兩周是8天,小月29天則有一周是8天,雖與西方固定的七天不同,但中國把日月五星謂之七政,也稱“七曜”,算是巧合吧。這么多進制,只有十進制最為流行,可能源于這和我們身體結(jié)構(gòu)相似有關,另外,八進制和十六進制最后在計算機中得到了廣泛使用。這些進制里面,只有二進制最為簡單,這也是計算機為何采用二進制而非其他進制的原因之一。當然,肯定不只這一個原因了,下面會有講。

擴展閱讀:古人的繩結(jié)計數(shù)法

古人用繩結(jié)計數(shù)

古埃及人圖形計數(shù)法

摩爾斯碼的發(fā)現(xiàn)

這里還有個重要的時間點需要提一下,就是1837年,也就是美國科學家莫爾斯發(fā)明的莫爾斯電碼,他嘗試用一些代碼比如: 點、劃、點和劃之間的停頓、每個字符間短的停頓(在點和劃之間)、每個詞之間中等的停頓以及句子之間長的停頓來表示不同的字母、數(shù)字和標點符號!

為何要提它呢?它本身和二進制是有區(qū)別的,但注意時間點,我認為后面發(fā)現(xiàn)二進制并誕生計算機和這個存在關系,而且它除了是一種重要的遠距離通訊手段外(二戰(zhàn)期間被大量使用),更重要的是有助于我們理解字符集的問題,這個文章后面會解釋到。

摩爾斯電碼表

摩爾斯電碼

摩爾斯電碼,實際上就是符號與字母數(shù)字的對應。

電報的原理是:“點”對應于短的電脈沖信號,“劃”對應于長的電脈沖信號,這些信號傳到對方,接收機把短的電脈沖信號翻譯成“點”,把長的電脈沖信號轉(zhuǎn)換成“劃”,譯碼員根據(jù)這些點劃組合就可以譯成英文字母,從而完成了通信任務。

這里每一個符號其實就是一個字符;
而這所有的字符的集合就叫做字符集;
“點”或“劃”與字符之間的對應關系即可以理解為字符編碼;
把字符表示為“點”或“劃”并對應為電脈沖信號的過程既是編碼;
譯碼員把接收機接收到的脈沖信號轉(zhuǎn)化成點劃后譯成字符的過程即為解碼。

擴展閱讀:
布萊葉盲文與二進制
電報機的原理

半導體特性的發(fā)現(xiàn)

  • 1833年,英國的巴拉迪發(fā)現(xiàn)硫化銀材料的電阻是隨著溫度的上升而降低。
  • 1839年,法國的貝克萊爾發(fā)現(xiàn)半導體和電解質(zhì)接觸形成的結(jié),在光照下會產(chǎn)生一個電壓,這是被發(fā)現(xiàn)的半導體的第二個特征-光生伏特效應。
  • 1874年,德國的布勞恩發(fā)現(xiàn)某些硫化物的電導與所加電場的方向有關,即它的導電有方向性,在它兩端加一個正向電壓,它是導通的;如果把電壓極性反過來,它就不導電,這就是半導體所特有的第三種特性-半導體的整流效應。同年,舒斯特又發(fā)現(xiàn)了銅與氧化銅的整流效應。
  • 1873年,英國的史密斯又發(fā)現(xiàn)了硒晶體材料在光照下電導增加。半導體的光電導效應被發(fā)現(xiàn)。

半導體特性的發(fā)現(xiàn)和應用使得人類從電力時代步入到了信息時代。這為計算機的產(chǎn)生提供了可能,為何這么說?因為半導體被發(fā)現(xiàn)的特性使得它能夠?qū)崿F(xiàn)電流的“開、關”功能(原理在下面),這是個很重要的發(fā)現(xiàn)。聰明的人類利用半導體材料發(fā)明了二極管,而且這個功能是計算機誕生的基石!

二極管的原理

上面說了,半導體可以實現(xiàn)電流“開、關”功能,其基本原理在于PN結(jié),即由帶正電的P型半導體(以空穴為載流子)和以帶負電的N型半導體(以電子為載流子)形成的界面結(jié)。

帶正電P型半導體

帶負電N型半導體

擴展資料:半導體中的兩種載流子-電子和空穴是如何產(chǎn)生和運動的?

  • 如果加P到N的正向電壓,則空穴和電子都將流向該區(qū)并中和對方的離子使得該區(qū)范圍不斷減小直至導通。


    PN結(jié)加正向電壓時導通
  • 如果加N到P的方向電壓,則空穴和電子朝反方向運動,空間電荷區(qū)將不斷增大,電流無法通過。
    PN結(jié)加反向電壓時截止

    現(xiàn)在我們把一個討厭電子的半導體(N型半導體)和一個喜歡電子的半導體(P型半導體)放在一起的時候,在它們之間就會形成一個空間電荷區(qū),也就是電子趨向的力量(半導體畢竟不是導體,半導體的原子核對電子的束縛力是足夠大的,因此雖然有趨向,但不足以讓一側(cè)的電子跑到另一側(cè) --- 這也就是為什么它叫做“半導體”),這就是一個二極管了。當二極管外部有電流通過的時候,如果外部電流內(nèi)的電子的運動方向順從了其內(nèi)部的這種電子的趨向,則這個二極管可以視為接通的導線;而如果外部電流的電子運動方向和二極管內(nèi)部的電子方向相反的時候,外部電流就無法通過這個二極管(除非加了很高的反向電壓 ----- 這樣就會將二極管擊穿),此時這個二極管可以視為斷路。所以在二極管的電路里,根據(jù)二極管兩端的電壓的高低的不同,就能測出某些電阻兩端的電壓有不同的數(shù)值:高或者低。就是說PN結(jié)就可以識別正反向電壓,或者通過不同方向電壓就可以控制電路的通或斷,這就實現(xiàn)了“是”與“非”的兩個狀態(tài)并可以對其加以控制,來判斷“0”與“1”這種狀態(tài),這樣就為實現(xiàn)二進制的邏輯運算提供了可能,盡管電子在電路中的運動速率要遠小于光速,但是建立電路關聯(lián)只和電場存在與否相關,電場是以近光速運動的,這就能實現(xiàn)電路中的快速響應也就等效于快速運算,這也是“電腦”能比人腦快得多的原因。
    動態(tài)原理圖

注意:這里的0和1只是邏輯表現(xiàn)方法,物理表示就是高電位和低電位,比如可以這樣規(guī)定:1就表示高電壓,0就表示低電壓。不要把0和1理解為阿拉伯數(shù)字中的0和1

擴展閱讀:
手電筒原理
繼電器原理

二進制的發(fā)現(xiàn)

對于二進制,目前世界數(shù)學界公認的是德國數(shù)理哲學大師萊布尼茨。對,就是那個和牛頓獨立發(fā)現(xiàn)微積分的大神。實際上,我國古人的智慧成果--八卦,就是使用陰陽兩種元素來表示天地萬物的。于是我搜了一下還真有資料,這里面還有個故事:

萊布尼茨在20多歲的時候就已經(jīng)接觸到了中國的《易經(jīng)》,后于1697年發(fā)明了二進制,但由于沒有強有力的證據(jù),也沒多少人理解,一直沒發(fā)表。后來31歲時又接觸到了古老《易經(jīng)》里伏羲畫的六十四卦卦符,發(fā)現(xiàn)和二進制的數(shù)碼相對應,這給了他極大的啟發(fā)和鼓舞,于1705年發(fā)表了二進制?!吨芤住窔w妹章就是講二進制的:二進制碼的每位數(shù)都是2的n次冪六三辭 “歸妹以須,反歸以娣,歸冪以須,反歸一遞,歸冪以須,數(shù)碼位數(shù)依須要而定,夠用為限,多了無益?!边@句話對今天的航天仍具指導意義。航天器上的空間比同體積的黃金還貴,儲存器的位數(shù)決定衛(wèi)星的造價。反歸一遞--卦符的上爻位是2的0次冪1的位置,因易的行文順序是由初爻(下爻,2的5次冪位)開始依次向上降冪:5、4、3、2、1、0 的順序,故說反歸一遞?!兑捉?jīng)》還記載:易有太級,是分兩儀,兩儀生四象,四象生八卦,八卦化萬物。后來,萊布尼茲也曾預言過八卦會引起世界性的變革?。ɡ献孀谂1瓢。?/code>

放個兩個鏈接:
八卦與二進制的關系
周易中的二進制數(shù)學

很可惜的是,二進制作為一種進位制的計數(shù)方法,誰發(fā)明并沒有多大差別,因為它和我們的十進制一樣并不是什么很高級的思想。上面講了,古人就在用進制,它只是人們規(guī)定的一種進位方法。對于任何一種進制---X進制,就表示某一位置上的數(shù)運算時是逢X進一位。十進制是逢十進一,十六進制是逢十六進一,二進制就是逢二進一,每一種數(shù)制都使用位置表示法。處于不同位置的數(shù)符所代表的值不同,與它所在位的權值有關。
十進制1234.55可表示為:
1234.55=1×103+2×102+3×101+4×100+5×10(-1)+5×10(-2)。
可以看出,各種進位計數(shù)制中權的值恰好是基礎的某次冪。因此,對任何一種進位計數(shù)制表示的數(shù)都可以寫成按權展開的多項式,也就說明各種進制之間可以相互轉(zhuǎn)化的。比如十進制中11這個數(shù)計算公式就是這樣的:1×10^1 + 1×10^0 = 11。二進制1011這個數(shù)計算公式就是這樣的:1011 = 1×2^3 + 0×2^2 + 1×2^1 + 1×2^0 = 8 + 0 + 2 + 1 = 11,不論是二進制1011還是十進制11,都代表同一個數(shù)字11。

二進制作為計數(shù)方法確實夠簡單,目前我們不可能找到比二進制數(shù)字系統(tǒng)更簡單的數(shù)字系統(tǒng)了(如果把數(shù)字1去掉,就只剩下0一個數(shù)字,只有一個數(shù)字0的數(shù)字系統(tǒng)是什么都做不成的)。雖然二進制作為計數(shù)方法并不驚艷,但二進制數(shù)字系統(tǒng)架起了算術與電之間的橋梁,我們把二進制跟布爾代數(shù),跟數(shù)字電路聯(lián)系起來后,則成就了計算機二進制理論,而計算機是影響人類的重大發(fā)明。

計算機中的二進制

自18世紀萊布尼茲發(fā)現(xiàn)二進制(基數(shù)為2,只用0和1兩個數(shù)表示,“逢二進一”,“借一當二”)后,人類開動自己的聯(lián)想,產(chǎn)生了很多有趣的關聯(lián)。比如上面我們了解的開關、電線、燈泡、繼電器、二極管等物體都可以表示二進制數(shù)0和1:
電線可以表示二進制數(shù)字。有電流代表數(shù)字1;沒有,則代表數(shù)字0。
開關可以表示二進制數(shù)字。開關閉合,代表數(shù)字1;開關斷開,代表數(shù)字0。
燈泡可以表示二進制數(shù)字。燈泡亮,代表數(shù)字1;沒亮,代表數(shù)字0。
電報繼電器表示二進制數(shù)字。繼電器閉合,代表數(shù)字1;繼電器斷開,代表數(shù)字0。
受到啟發(fā)后,聰明的人類將二進制就和二極管產(chǎn)生了聯(lián)系,再后來又腦洞大開,決定用8個可以開合的晶體管(一個開關,用“開”來表示1,“關”來表示0)來組合成不同的狀態(tài),以表達更多的信息。后來,他們看到8個開關運行狀態(tài)正常后,給這8個狀態(tài)起了個名字叫--“字節(jié)”。再后來,他們覺得8個狀態(tài)的字節(jié)還可以組合出更多狀態(tài)來,表達更多的信息,于是他們又做了一些可以處理這些“字節(jié)”的機器,在看到他們也運轉(zhuǎn)正常后,覺得這一定是上帝的旨意。于是,他們給這個機器又起了一個非常牛逼的名字--“計算機”(當時一臺計算機足有一間半教室那么大,六頭大象的重量)。所以,我們明白了計算機中的二進制實際上就是一個非常微小的開關,用“開”來表示1,“關”來表示0,0和1各占一個“位”,用8個不同狀態(tài)的0和1表示“字節(jié)”,通過“字節(jié)”組合不同的狀態(tài)來存儲和運算不同數(shù)據(jù)。

擴展閱讀:
第一代計算機-電子管計算機
第二代計算機-晶體管計算機

計算機中為什么要用二進制而不是其他進制?

計算機使用二進制是由它的實現(xiàn)機理決定的。上面說過,電腦的基層部件是由集成電路組成的,這些集成電路可以看成是一個個門電路組成,(當然事實上沒有這么簡單的)。當計算機工作的時候,電路通電工作,于是每個輸出端就有了電壓。電壓的高低通過模數(shù)轉(zhuǎn)換即轉(zhuǎn)換成了二進制:高電平是由1表示,低電平由0表示。也就是說將模擬電路轉(zhuǎn)換成為數(shù)字電路。這里的高電平與低電平可以人為確定,一般地,2.5伏以下即為低電平,3.2伏以上為高電平。電子計算機能以極高速度進行信息處理和加工,包括數(shù)據(jù)處理和加工,而且有極大的信息存儲能力。數(shù)據(jù)在計算機中以器件的物理狀態(tài)表示,采用二進制數(shù)字系統(tǒng),計算機處理所有的字符或符號也要用二進制編碼來表示。用二進制的優(yōu)點主要在于:

  • 技術實現(xiàn)簡單,計算機是由邏輯電路組成,邏輯電路通常只有兩個狀態(tài),開關的接通與斷開,這兩種狀態(tài)正好可以用“1”和“0”表示。
  • 簡化運算規(guī)則:兩個二進制數(shù)和、積運算組合各有三種,運算規(guī)則簡單,有利于簡化計算機內(nèi)部結(jié)構(gòu),提高運算速度。
  • 適合邏輯運算:邏輯代數(shù)是邏輯運算的理論依據(jù),二進制只有兩個數(shù)碼,正好與邏輯代數(shù)中的“真”和“假”相吻合。
  • 易于進行轉(zhuǎn)換:二進制與十進制數(shù)易于互相轉(zhuǎn)換。
  • 抗干擾能力強:因為每位數(shù)據(jù)只有高低兩個狀態(tài),當受到一定程度的干擾時,仍能可靠地分辨出它是高還是低,可靠性高。

除了以上優(yōu)點外,采用二進制還有個客觀原因在于,人們知道,具有兩種穩(wěn)定狀態(tài)的元件(如晶體管的導通和截止,繼電器的接通和斷開,電脈沖電平的高低等)在那個時代容易找到,而要找到來對應十進制的10個數(shù)的具有10種穩(wěn)定狀態(tài)的元件在那個時代就非常困難了。這也是計算機為何采用二進制而非人類更熟悉的十進制的原因吧。

為什么又會出現(xiàn)八進制、十六進制?

人類一般思維方式是以十進制來表示的,而計算機則是二進制,但是對于編程人員來說,都是需要直接與計算器打交道的,如果給我們一大串的二進制數(shù)。比如說一個4個字節(jié)的int型的數(shù)據(jù):0000 1010 1111 0101 1000 1111 11111 1111,我想任何程序員看到這樣一大串的0、1都會很蛋疼。所以必須要有一種更加簡潔靈活的方式來呈現(xiàn)這對數(shù)據(jù)了。你也許會說,直接用十進制吧,如果是那樣,就不能準確表達計算機思維方式了(二進制),所以,出現(xiàn)了八進制、十六進制,其實十六進制應用的更加廣泛,就比如說上面的int型的數(shù)據(jù),直接轉(zhuǎn)換為八進制的話,32./3 余2 也就是說,我們還要在前面加0,但是轉(zhuǎn)換為十六進制就不同了。32/4=8,直接寫成十六進制的8個數(shù)值拼接的字符串,簡單明了。所以說用十六進制表達二進制字符串無疑是最佳的方式,這就是八進制和十六進制出現(xiàn)的原因。

擴展閱讀:各進制之間如何轉(zhuǎn)換

進制間的相互轉(zhuǎn)換問題

  • 八進制、十六進制、二進制轉(zhuǎn)為十進制
    都是按權展開的多項式相加得到十進制的結(jié)果。
    比如:
    二進制1010.1到十進制:1×2^3 + 0×2^2 + 1×2^1 + 0×2^0 + 1×2^(-1)=10.5
    八進制13.1到十進制:1×8^1 + 3×8^0 + 1×8^(-1)=11.125
    十六進制13.1到十進制:1×16^1 + 3×16^0 + 1×16^(-1)=19.0625
  • 十進制轉(zhuǎn)為八進制、十六進制、二進制
    都是按照整數(shù)部分除以基數(shù)(r)取余,小數(shù)部分乘以基數(shù)(r)取整。
    十進制10.25 到二進制:整數(shù)部分除2,一步步取余。小數(shù)部分乘2,一步步取整。
    進制轉(zhuǎn)化

    八進制到十進制,十六進制到十進制都是和上面的一樣,只不過不在是除2乘2,而是8或者16了,這是根據(jù)自己的基數(shù)來決定的。
  • 二進制轉(zhuǎn)為八進制、十六進制
    二進制轉(zhuǎn)換成八進制的方法是:從小數(shù)點起,把二進制數(shù)每三位分成一組,小數(shù)點前面的不夠三位的前面加0,小數(shù)點后面的不夠三位的后面加0,然后寫出每一組的對應的十進制數(shù),順序排列起來就得到所要求的八進制數(shù)了。
    依照同樣的思想,將一個八進制數(shù)的每一位,按照十進制轉(zhuǎn)換二進制的方法,變成用三位二進制表示的序列,然后按照順序排列,就轉(zhuǎn)換為二進制數(shù)了。
    二進制數(shù)10101111.10111轉(zhuǎn)換為八進制的數(shù):
    (010 101 111.101 110)= 2 5 7.5 6=257.56
    八進制數(shù)257.56轉(zhuǎn)換為二進制的數(shù):
    257.56 =(010 101 111.101 110)=10101111.101
    二進制轉(zhuǎn)換到十六進制差不多:從小數(shù)點起,把二進制數(shù)每四位分成一組,小數(shù)點前面的不夠四位的前面加0,小數(shù)點后面的不夠四位的后面加0,然后寫出每一組的對應的十進制數(shù),然后將大于9的數(shù)寫成如下的形式:
    10---->A,11--->B,12---->C,13---->D,14--->E,15--->F
    在順序排列起來就得到所要求的十六進制了。
    同樣,將一個十六進制數(shù)的每一位,按照十進制轉(zhuǎn)換二進制的方法,變成用四位二進制表示的序列,然后按照順序排列,就轉(zhuǎn)換為二進制數(shù)了。
    二進制數(shù)10101111.10111轉(zhuǎn)換為十六進制的數(shù):
    (1010 1111.1011 1000)=A F.B 8=AF.B8
    十六進制AF.B8轉(zhuǎn)換為二進制:
    AF.B8=(1010 1111.1011 1000)=10101111.10111

計算機中二進制何以表達萬物?它是如何讀懂二進制碼的?

計算機是怎樣用二進制0和1來表達信息的,甚至號稱可以表達宇宙萬物所有信息呢?
我們知道編程語言里的基本數(shù)據(jù)類型,都是以符合人類世界和自然世界的邏輯而出現(xiàn)的,便于讓人更容易理解,可以說,這些數(shù)據(jù)類型是架通人類思維與計算機的橋梁。
但是我們同時也知道,依照馮諾依曼體系,計算機中并沒有這些數(shù)據(jù)類型,而全部都是0和1表示的二進制數(shù)據(jù),并且計算器只能理解這些0和1的數(shù)據(jù)。既然,所有的數(shù)據(jù)在計算機里面都是以0和1存儲和運算的,那么,符合我們?nèi)祟愃季S的數(shù)據(jù)則都要通過一定的轉(zhuǎn)換才能被正確的存儲到計算機中。轉(zhuǎn)化的原理是什么呢?
舉個栗子:假如你看上一個學妹,你想表白,所以你送了她一件禮物,如果學妹接受了禮物表示接受你,決絕了禮物表示拒絕你。也就是我們可以用0和1來表達學妹的態(tài)度(實際情況可能更復雜)。
再舉個栗子:有兩個人A和B去看了一個電影,怎么用0和1表示呢?我們用0代表討厭,1代表喜歡。會出現(xiàn):
00 = AB都不喜歡這影片
01 = A討厭它,B喜歡它
10 = B喜歡它,A討厭它
11 = AB都很喜歡這影片
如果加上我的觀點呢,它就變成2^3次方=8種可能了。如:000、001、010、011、100、101、110、111,所以,計算機的二進制雖然無法和人一樣理解那么多,但有了這0和1,計算機就可以通過“開”和“關”的狀態(tài)來做二元邏輯判斷(真假、對錯、是否等非黑即白的問題)了,不要小看這二元邏輯判斷,不客氣的說它確實可以表達宇宙萬物(道德經(jīng):道生一,一生二,二生三,三生萬物)!我們不要忘記,發(fā)明二進制的萊布尼茲不但是個數(shù)學家,還是個牛逼的哲學家啊,他一直試圖用數(shù)學符號和操作符來表達亞里士多德的邏輯理論,而且他也是最接近這個目標的人,直到后來喬治·布爾在概念上的突破成果--布爾代數(shù)的出現(xiàn)。

大部分計算機內(nèi)部的連接是一樣的,都是通過電氣信號來進行通信的,也就是電壓高低的變化,這些電流會經(jīng)過 MOSFET 晶體管,晶體管中包含 N 型半導體和 P 型半導體,通過電壓就能控制線路開閉,然后這些 MOSFET 構(gòu)成了 CMOS,接著再由 CMOS 實現(xiàn)「與」「或」「非」等邏輯電路門,最后由邏輯電路門上就能實現(xiàn)加法、位移等計算。

配套邏輯運算:與、或、非。

  • 與:只要有0結(jié)果就是0。比如1與0等于0。
  • 或:只要有1結(jié)果就是1。比如1與0等于1。
  • 非:翻轉(zhuǎn)。0變1、1變0。
    還有其他邏輯運算如同或、異或等。
    后來,有了算法,而算法中又有三種基本結(jié)構(gòu)方式:順序、選擇、循環(huán)。
    而這三種結(jié)構(gòu)可以涵蓋所有情況,沒有例外。舉個栗子:
a---餓了想要吃點兒東西。
b---循壞進食的動作10次。
c---如果吃飽了,到d;否則,到b。
d---結(jié)束進食的動作。

如果這還不足以說服你,只能建議你去研究圖靈機了。圖靈機處理信息遠比這個復雜的多。還以上面的舉例:

a---餓了想要吃點兒東西。
b---循壞進食的動作10次。(該減肥了?是則掛起,否則繼續(xù))
c---如果吃飽了,到d;否則,到b。
d---結(jié)束進食的動作。

如果b反復執(zhí)行到一半的時候,你發(fā)現(xiàn)自己是不是該考慮減肥了。如果是,這時候圖靈機就會立刻優(yōu)先對這個新的減肥輸入進行處理而會將循環(huán)進食的動作掛起,如果不是,則喚醒剛才被掛起的動作,繼續(xù)進行。也就是說我們每個人都是一個非常非常復雜的圖靈機,上面只是一種情況,而人的情況指令要比這個例子復雜的多,這也是為什么人工智能總是達不到真實人的程度,當然,如果這個理論正確,假設我們也是高級文明創(chuàng)造的復雜版的圖靈機的話,誰又能保證人工智能不會進化呢?所以,這個宇宙中的一切,都可以抽象成圖靈機,二進制可以表達一切!

圖靈機簡圖

電影-圖靈

雖然,這個世界上的所有事件,都可以用順序、選擇、循環(huán)三種邏輯來表示。雖然二進制可以表達宇宙萬物。但是,可以表達一切現(xiàn)象并不代表可以解決一切問題。
例如:顯示自己本身的所有代碼。
比如有這樣一個程序,就一行:

顯示(100)

這個程序就做一件事情:顯示數(shù)字100。
問題來了,你如何把“顯示(100)”這兩個漢字一組括號和一個數(shù)字100顯示出來呢?
似乎應該這樣:

顯示(顯示(100))

但是其實當你這樣做的時候,你已經(jīng)改變了程序本身,現(xiàn)在的程序為兩個“顯示”兩組括號和一個100,而你運行程序后只能顯示一個“顯示”一組括號和一個100,很顯然你并沒有把這個程序全部顯示出來。

類似這樣的問題,就是計算機無法解決的問題(好像類似先有蛋還是先有雞的問題)。

再比如“背包問題-貪心算法”(一種組合優(yōu)化的NP完全問題):
一個小偷去博物館偷東西。博物館內(nèi)有3件物品。

第一件物品的重量為3, 價值為4;
第二件物品的重量為2, 價值為3;
第三件物品的重量為4, 價值為5;
小偷的背包最多可以承受的重量為7。

請問:應該如何拿物品,可以讓背包里的物品總價值最大(不能重復裝入)?
這個問題似乎非常簡單,稍微比較后就會得出結(jié)論:拿第一件和第三件物品。

但是,如果我們擴大一下問題:如果有6件物品、背包可以承受的重量也提高一些,應該怎么拿?這個時候如果讓計算機去解決這個問題,我們就會發(fā)現(xiàn)當問題的規(guī)模擴大1倍后,計算機所需要的時間卻擴大了遠不止1倍 --- 即解決問題所需時間不再是線性變化了。

就拿這個問題來說,如果物品數(shù)量超過十幾個,那么計算機要想解決這個問題所需要的時間就已經(jīng)超過了100年。

這類問題是計算機或者說用那三種邏輯順序可以解決的、但是所需要的時間是人類無法容忍的。

擴展閱讀:
邏輯門電路
邏輯與、或、非
認識圖靈機
數(shù)學危機與圖靈機
圖靈機視頻
如何自己造一臺8位計算機

二進制和編程有什么關系?

計算機讀取二進制編碼,不是單純的二進制數(shù),而是嚴格按照一定的格式來編碼的。讀指令這些操作都是靠計算機里的CPU來進行的,按照馮諾依曼體系,CPU里有一個控制寄存器(一堆代表0和1的開關)。這樣我們可以用一組二進制數(shù)來描述這個控制寄存器的狀態(tài)。開關的多少就是一條指令的長度,我們經(jīng)常見的32位機,就是一條指令長為32,就是這個控制有32個小開關。同樣64位機有64個。在寄存器內(nèi),各個位有的是單獨工作的,有的是好幾個一起工作的,我們把他們分成一個個小組。有的組是表示狀態(tài)的,只能讀不能寫,或者說寫了也沒用。而有的組是可以操作的。而且這些組是可以在一定條件下改變的,用來實現(xiàn)某個功能。對于那些可以操作的寄存器是有特定功能的,比如說有個開關你撥一下,它就伸出個腿什么的,你撥回去它就縮回來。這樣我們按照上面的格式寫給CPU一串二進制編碼,他就能按照我們意愿完成某個動作。這個編碼格式其實就跟你喊:電腦,電腦,給我個雞腿!或者:媽,我餓了!是一樣一樣的。這些二進制0和1的編碼動輒幾十位,電腦是好認了,可是讓程序員怎么讀?既然上面說的格式固定,那就用一些字符來代替寄存器組吧!這樣匯編語言就誕生了,當然匯編是按照機器的模式來的,還是不好理解,那C語言等等一些語言就出來了。而我們也有代碼寫了!程序員干的工作實際上就是,為了讓計算機解決某個問題而使用某種程序設計語言編寫程序(編譯后的代碼集合)來和計算機溝通,并最終得到相應結(jié)果的過程。程序員寫好的程序會按照CPU能認識的格式來生成二進制文件,每條指令的長度固定。這樣CPU每次讀一點,按照讀出的指令來完成相應的動作,然后再讀下一條。這樣就可以完成程序交給它的任務了。當然不同的CPU的控制寄存器個數(shù)不相同,但是大致原理都差不多。也就是說,程序員干的事實際就是在和計算機約會。(程序員沒有女朋友難道是這個原因?)

CPU結(jié)構(gòu)

內(nèi)存是怎么工作的?

  • DRAM基本組成
    內(nèi)存是由DRAM(動態(tài)隨機存儲器)芯片組成的。DRAM的內(nèi)部結(jié)構(gòu)可以說是PC芯片中最簡單的,是由許多重復的“單元”——cell組成,每一個cell由一個電容和一個晶體管(一般是N溝道MOSFET)構(gòu)成,電容可儲存1bit數(shù)據(jù)量,充放電后電荷的多少(電勢高低)分別對應二進制數(shù)據(jù)0和1。由于電容會有漏電現(xiàn)象,因此過一段時間之后電荷會丟失,導致電勢不足而丟失數(shù)據(jù),因此必須經(jīng)常進行充電保持電勢,這個充電的動作叫做刷新,因此動態(tài)存儲器具有刷新特性,這個刷新的操作一直要持續(xù)到數(shù)據(jù)改變或者斷電。而MOSFET則是控制電容充放電的開關。DRAM由于結(jié)構(gòu)簡單,可以做到面積很小,存儲容量很大。

    內(nèi)存cell結(jié)構(gòu)

  • 內(nèi)存地址
    內(nèi)存中的cell按矩陣形排列,每一行和每一列都會有一個對應的行地址線路(正規(guī)叫法叫做word line)和列地址線路(正規(guī)叫法是bit line),每個具體的cell就掛接在這樣的行地址線路和列地址線路上,對應一個唯一的行號和列號,把行號和列號組合在一起,就是內(nèi)存的地址。

    SPD dump

    上圖是Thaiphoon Burner的一個SPD dump,每個地址是一個字節(jié)。不過我們可以把這些數(shù)據(jù)假設成只有一個bit,當成是一個簡單的內(nèi)存地址表,左邊豎著的是行地址,上方橫著的是列地址。例如我們要找第七行、倒數(shù)第二列(地址為7E)的數(shù)據(jù),它就只有一個對應的值:FD。當然了,在內(nèi)存的cell中,它只能是0或者1。計算機會把所有的信息都給數(shù)字化,所以它知道自已把一個數(shù)據(jù),一條命令記到了內(nèi)存中的哪個(些)位置。
    充放電存儲


    內(nèi)存地址是內(nèi)存當中存儲數(shù)據(jù)的一個標識,并不是數(shù)據(jù)本身,通過內(nèi)存地址可以找到內(nèi)存當中存儲的數(shù)據(jù)。

  • 尋址
    數(shù)據(jù)要寫入內(nèi)存的一個cell,或者從內(nèi)存中的一個cell讀取數(shù)據(jù),首先要完成對這個cell的尋址。尋址的過程,首先是將需要操作的cell的對應行地址信號和列地址信號輸入行/列地址緩沖器,然后先通過行解碼器(Row Decoder)選擇特定的行地址線路,以激活特定的行地址。每一條行地址線路會與多條列地址線路和cell相連接,為了偵測列地址線路上微弱的激活信號,還需要一個額外的感應放大器(Sense Amplifier)放大這個信號。當行激活之后,列地址緩沖器中的列地址信號通過列解碼器(Column Decoder)確定列地址,并被對應的感應放大器通過連接IO線路,這樣cell就被激活,并可供讀寫操作,尋址完成。從行地址激活,到找到列地址這段時間,就是tRCD。

    尋址

舉例說明

內(nèi)存地址

擴展閱讀:
內(nèi)存工作原理

計算機如何存儲數(shù)據(jù)的?

數(shù)據(jù)類型一般分類基本數(shù)據(jù)類型和復合數(shù)據(jù)類型,復合數(shù)據(jù)類型也是由基本數(shù)據(jù)類型構(gòu)成的。所以,這里先只討論基本數(shù)據(jù)類型(無符號整形,帶符號整形,實型,char型)的存儲情況。

  • 存儲無符號整形:無符號整形在數(shù)據(jù)中的存儲無疑是最方便的,因為沒有符號位,只表示正數(shù),所以在存儲計算方面都很簡單。無符號整形在就是以純粹的二進制串存儲在計算機中的。比如說看下面的例子:

    從輸出的十六進制數(shù)中可以看出,它就是以直接的二進制數(shù)表示的。
  • 存儲帶符號整形:對于帶符號數(shù),機器數(shù)的最高位是表示正、負號的符號位,其余位則表示數(shù)值。先不談其他的問題,只談二進制表達數(shù)據(jù)的問題,看下面的例子:
    假設機器字長為8的話:
    一個十進制的帶符號整形1,表達為二進制就是(0000 0001)
    一個十進制的帶符號整形-1,表達為二進制就是(1000 0001)
    那么,兩者相加 ,用十進制運算 1+(-1)=0
    在看二進制運算(0000 0010)+(1000 0001)=(1000 0010)這個數(shù)轉(zhuǎn)換為十進制結(jié)果等于-2??梢园l(fā)現(xiàn)出問題了,這就是今天所要講的原碼。
    原碼
    數(shù)值X的原碼記為[x]原,如果機器字長為n(即采用n個二進制位表示數(shù)據(jù))。則最高位是符號位。0表示正號,1表示負號,其余的n-1位表示數(shù)值的絕對值。數(shù)值0的原碼表示有兩種形式:[+0]原=0000 0000,-[0]原=1000 0000。
    例子:若機器字長n=8,則
    [+1]原=0000 0001 [-1]原=1000 0001
    [+127]原=0111 1111 [-127]原=1111 1111
    [+45]原=0010 1101 [-45]原=1010 1101
    可見,原碼,在計算數(shù)值上出問題了,當然,你也可以實驗下,原碼在計算正數(shù)和正數(shù)的時候,它是一點問題都沒有的,但是出現(xiàn)負數(shù)的時候就出現(xiàn)問題了。所以還需要講一個問題:反碼
    反碼
    數(shù)值X的反碼記作[x]反,如果機器字長為n,則最高位是符號位,0表示正號,1表示負號,正數(shù)的反碼與原碼相同,負數(shù)的反碼則是其絕對值按位求反。數(shù)值0的反碼表示有兩種形式:[+0]反=0000 0000,-[0]反=1111 1111。
    例子:若機器字長n等于8,則
    [+1]反=0000 0001 [-1]反=1111 1110
    [+127]反=0111 1111 [-127]反=1000 0000
    [+45]反=0010 1101 [-45]反=1101 0010
    在看反碼計算的問題:
    1+(-1)=0 |(0000 0001)反+(1111 1110)反=(1111 1111)反=(1000 0000)原=【-0】可以看到,雖然是-0,但是問題還不是很大。
    1+(-2)=-1 |(0000 0001)反+(1111 1101)反=(1111 1110)反=(1000 0001)原=【-1】 可以看到,沒有問題。
    -1+(2)=1 |(1111 1110)反+(0000 0010)反=(0000 0000)反=(0000 0000)原=【0】可以看到,問題發(fā)生了,因為溢出,導致結(jié)果變?yōu)?了。
    所以,看以看到,用反碼表示,問題依然沒有解決,所以,還需要一個東西-補碼。
    補碼
    數(shù)值X的補碼記作[x]補,如果機器字長為n,則最高位是符號位,0表示正號,1表示負號,正數(shù)的補碼與原碼反碼都相同,負數(shù)的補碼則等于其反碼的末尾加1。數(shù)值0的補碼表示有唯一的編碼:[+0]補=0000 0000,-[0]補=0000 0000。
    例子:若機器字長n等于8,則
    [+1]補=0000 0001 [-1]補=1111 1111
    [+127]補=0111 1111 [-127]補=1000 0001
    [+45]補=0010 1101 [-45]補=1101 0011
    在看補碼計算的問題:
    1+(-1)=0 |(0000 0001)補+(1111 1111)補=(0000 0000)補=(0000 0000)原=【0】 可以看到,沒有問題。
    1+(-2)=-1 |(0000 0001)補+(1111 1110)補=(1111 1111)補=(1000 0001)原=【-1】可以看到,沒有問題。
    -1+(2)=1 |(1111 1111)補+(0000 0010)補=(0000 0001)補 =(0000 0001)原=【1】 可以看到,沒有問題。
    通過上面的計算,我們發(fā)現(xiàn),用補碼的方式,就不存在在原碼和反碼中存在的計算問題了。其實,這也是計算機表達帶符號整數(shù)用補碼的原因。補碼一定是不會存在原碼和反碼的問題的。
    討論下原碼反碼補碼的原理,沒興趣的可以跳過 。
  1. 為什么原碼不行?
    ( 1 )10- ( 1 )10 = ( 1 )10 + ( -1 )10 =( 0 )10
    (00000001)原+ (10000001)原 = (10000010)原 = ( -2 ) 顯然不正確。
    通過上面原碼計算式可以看出,當正數(shù)加上負數(shù)時,結(jié)果本應是正值,得到的卻是負值(當然也有可能得到的是正數(shù),因為被減數(shù)與減數(shù)相加數(shù)值超過0111 1111,即127,就會進位,從而進位使符號位加1變?yōu)?了,這時結(jié)果就是正的了)。而且數(shù)值部分還是被減數(shù)與減數(shù)的和。并且,當負數(shù)加上負數(shù)時(這里就拿兩個數(shù)值部分加起來不超過0111 1111的來說),我們可以明顯看出符號位相加變?yōu)?,進位1被溢出。結(jié)果就是正數(shù)了。因此原碼的錯誤顯而易見,是不能用在計算機中的。
  2. 補碼的原理
    既然原碼并不能表示負數(shù)的運算問題,那么當然要另想他法了。這個方法就是補碼,不得不說,這是一個最簡潔的方法,當然,也可以用別的更復雜的方法,那就不是我們想要的了。我自己研究補碼的時候,也在網(wǎng)上找了些資料,都是到處copy,反正我是看的迷糊了,本人數(shù)學功底不怎么樣,看不懂那些大神寫的,只好,自己理解了下。要談補碼,先看看補數(shù)的問題。什么是補數(shù),舉個簡單的例子,100=25+75。100用數(shù)學來說就是模M,那么就可以這樣概括。在M=100的情況下,25是75的補數(shù)。這就是補數(shù)。25是75的補數(shù),這是在常規(guī)世界中,在計算機上就不是這樣了,因為,在計算機中,數(shù)據(jù)存在溢出的問題。假設機器字長是8的話,那么能表達的最大無符號數(shù)就是1111 1111,在加1的話,就變成1 0000 0000 ,此時因為溢出,所以1去掉,就變成0了,
    也就是說,在計算機中,補數(shù)的概念稍微不同于數(shù)學之中,25+75=100,考慮計算機中的溢出問題,那么25+75就等于0了。也就是說,25和75不是互為補數(shù)了。我覺得用鬧鐘來比喻這個問題再形象不過了,因為鬧鐘也存在著溢出的問題,當時間到達11:59 ,在加1分鐘的話就變成0:0了,這和計算機的溢出是同一個道理。那么,有一個時鐘,現(xiàn)在是0點,我想調(diào)到5點,有兩種方法,一個是正著撥5,到5點。第二種方法是倒著撥7,也可以到5點。正著撥5記作+5,倒著撥7,記作-7,而鬧鐘的M是12,也就是說,在考慮溢出的情況下,M=12,5是-7的補數(shù)。用個數(shù)學等式可以這樣表0+5=0+-7,即0+5=0-7,這就是計算機中的數(shù)值計算和數(shù)學中的計算不同的地方。
    明白了計算機中補數(shù)的道理,那么就明白補碼的問題了。還是用例子說明:
    在計算機中計算十進制 1+(-2)。
    1的原碼是:0000 0001
    -2的原碼是:1000 0010
    -2的補碼是:1111 1110 這個二進制換做無符號的整數(shù)大小就是254,而8位二進制數(shù)的M=28=256。(很多文章中把M寫成27,這根本就是不對的,根本沒有解決符號位的問題)你發(fā)現(xiàn)什么了沒?當換成補碼后,-2和254就是補數(shù)的關系。也就是1+(-2)等價于了1+254了。這樣做,好處在什么地方,你自己都可以看得到:
    1):利用補數(shù)和溢出的原理,減法變成了加法。
    2):符號位不在是約束計算的問題,不會存在原碼中的問題了,因為變成補碼后,雖然最高位依然是1,但是這個1就不在是做為符號位了,而是作為一個普通的二進制位,參與運算了。
    所以,這就是補碼的原理所在,通過補數(shù)和溢出,解決了減法和負數(shù)問題。
  3. 十進制數(shù)求補碼,補碼求十進制數(shù)
    十進制求補碼:
    如果是正數(shù),直接求它的原碼,符號位為0
    如果是負數(shù),比較好的方法是先求十六進制,在由十六進制求二進制,符號位為1,在除了符號位都取反,在加1,即可得到補碼。
    補碼就十進制 :
    根據(jù)符號位判斷,如果符號位是0,表示是正數(shù),就是原碼,直接轉(zhuǎn)換就十進制即可。
    如果符號為是1,表示是負數(shù)。那么,連符號位在內(nèi)都取反,在加1,將該二進制轉(zhuǎn)換為十進制,該十進制數(shù)即使該負數(shù)的絕對值,加個負號-,就得到該負數(shù)。
  • 存儲帶小數(shù)點的數(shù)
    這個看了很多,還是迷糊,書讀的少,只是知道是通過浮點解決的,我就放兩個鏈接吧,感興趣的自己去研究。
    計算機內(nèi)存中浮點數(shù)的表示
    小數(shù)在計算機中的存儲形式

  • 存儲字符:主要通過字符編碼來解決,這也是我們要解決的問題。受限于篇幅,我把字符編碼剩下的部分放到第二篇文章來講了。

點擊接著閱讀

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容