轉戰物聯網·基礎篇04-不可不知的進制關係與數據傳輸的本質

  提及進制,你們都能想到二進制、10進制、16進制、8進制等等,可是在互聯網應用開發中,卻不多用到這些換算。在物聯網短指令應用中,卻十分常見。程序員

  理解字節本質和二進制編程

  不管是互聯網應用仍是物聯網應用,在網絡傳輸層傳送的其實都是二進制數據。由於現代通訊設備對信號處理都是用的數字電路,數字電路的輸入輸出只有兩種狀態,那就是高電平或低電平,也就是對應二進制數據的1和0。按照必定的時序和時鐘基準,就能夠表明了不一樣的信息。理解二進制數據的造成對物聯網開發的深刻是頗有必要的。
網絡

  上圖中綠色線表明數據電平,紅色線表明用於鑑別二進制每個數據位的時間基準。編程語言

  在前面咱們已經說到網絡傳輸中是按照字節傳輸的,而每一個字節一般是按照8位二進制組成的,那麼上圖說明了一個字節的數據在數字電路中電平變化狀況,也是在網絡傳輸中各個通訊設備間傳輸的電平變化狀況。綠色線是數據線上的電平,能夠看到有兩個凸起的高電平,高電平表明1,低電平表明0,那麼用二進制表示,是否是就是「01010」呢,事實上是不對的。上圖的一個字節的二進制值應該是「10011000」,爲何會識別多識別出來幾個位呢?緣由是還有一個時間間隔的基準參照,就是時鐘線上的電平變化。編輯器

  每次從低電平變化爲高電平,再從高電平變化爲低電平,這個過程產生的電平波形是個凸起的方波,咱們稱之爲高脈衝(因像脈搏跳動同樣而得名,也有用低脈衝的,原理相同)。時鐘線上接二連三產生着固定時間間隔和脈衝寬度的高脈衝,而數字電路在每一個時鐘高脈衝到來的時候,去識別一下數據線上的電平高低,若是是高就表明當前數據爲是1,不然爲0。就這樣每個時鐘高脈衝都去識別一次,將結果依次記錄下來,就組成了二進制數據。上圖中數據線上的第二個高電平被識別成二進制「11」,就是由於在數據線高電平期間經歷了兩個時鐘高脈衝,因此是兩個1,而不是一個1,數據線上的低電平被識別成多個0的原理也是同樣的。調試

  一個字節由8個二進制位組成,一般標準是高位在前,最低位序號是0,最高位序號是7,因此上圖中時鐘高脈衝上面的數字是描述一個字節中的8個位的序號,也就是順序。傳完一個字節緊接着傳下一個字節,原理相同。code

  爲何叫二進制?咱們知道一般生活中使用的數字計數是10進制的,因逢10進1而得名。那二進制也是由於逢2進1而得名的。一個字節的8位二進制實際就是一個計數標記,由8個位組合在一塊兒表示。由於每一個位只能有0和1兩種變化,因此要計數到2的時候就得進位了。看下面一組數據:blog

0000 0000       //這個字節的值是0,也就是10進制的0,中間空格爲書寫描述方便,實際不存在
0000 0001       //這個字節的值是1,也就是10進制的1
0000 0010       //這個字節的值是2,也就是10進制的2,需進位
0000 0011       //這個字節的值是3,也就是10進制的3
0000 0100       //這個字節的值是4,也就是10進制的4,需進位
0000 0101       //這個字節的值是5,也就是10進制的5
......
1111 1111       //這個字節的值是255,也就是10進制的255

  經過上面一組數據,就能看出逢2進1的關係了,也知道爲何一個字節的值是在0-255之間了。那麼8個位中,只有一個位爲1,其餘位爲0的時候,分別在不一樣位置時,他們的值是怎樣的關係呢,看下面一組數據:內存

0000 0001       //這個字節的值是1
0000 0010       //這個字節的值是2
0000 0100       //這個字節的值是4
0000 1000       //這個字節的值是8
0001 0000       //這個字節的值是16
0010 0000       //這個字節的值是32
0100 0000       //這個字節的值是64
1000 0000       //這個字節的值是128

  從上面這組數據能夠看出,值爲1的位,每前進1位,其值就會是以前的2倍。這與10進制的一、十、100等每前進1位值就是以前的10倍是同樣的規律,這樣更好理解二進制位變化的關係了。那麼咱們在代碼中,定義一個整數變量,賦值以後,它具體的二進制是什麼樣的呢,再看下面例子:開發

// C語言代碼
unsigned char tmp = 65;

  定義了一個無符號8位整數(單字節)變量 tmp,並同時給賦了初值爲10進制整數65,運行時這個變量的初值的二進制(也是內存中實際保存的形式)是「0100 0001」,由於這個二進制對應的10進制值就是65。而不是保存6和5這兩個數字的ASCii值(54,53)到內存中的。

  那麼單字節最大值是255,大於255的值是怎麼表示的呢,各個語言都有不一樣的整數數據類型,他們對應的字節數量是不同的,看下面的例子:

1111 1111                                   //255,無符號單字節整數最大值
1111 1111  1111 1111                        //65535,無符號雙字節整數最大值
1111 1111  1111 1111  1111 1111  1111 1111  //4294967295,無符號四字節整數最大值

  根據上面這組數據,在你使用的開發語言的數據類型中找到該數據類型是多少個字節的,就知道他能夠最大記錄的數值是多少了。

  對於整數變量是這樣記錄的,可是字符這類的是如何記錄的呢,這就不得不提ASCii碼(美國信息交換標準代碼)。ASCii碼將常見的英文字母、阿拉伯數字、英文標點符號等可見字符和經常使用的非可見控制符號都定義了對應的二進制值,每一個符號佔用一個字節,具體可網絡搜索「ASCii碼」有完整介紹,這裏就不用詳細介紹了。定義字符變量後,實際的變量二進制值是什麼,看下面的例子:

// C語言代碼
unsigned char strA = 'A';           //變量二進制值:0100 0001;10進制值:65
unsigned char str1 = '1';           //變量二進制值:0011 0001;10進制值:49

  上例中變量str1的值爲何是49而不是1,是由於這個‘1’是字符1,也就至關因而咱們寫字描述數量用的標識符號1,而不是計數時候的整數值自己,因此要使用字符‘1’的ASCii碼值49來表示出來,對應的二進制爲「0011 0001」。還有個差異,這個變量的值實際是整數49,可是在系統調試輸出的時候,會顯示出字符‘1’的,也就是可見的。而真正的那個整數1是須要轉換後才能夠看到他的值的,不然不可見或看到的是錯誤的或亂碼。大寫字母A的ASCii碼值是65,因此變量strA實際記錄的也是整數65的二進制值。

  理解十六機制

  理解了字節的本質是二進制組成的,也知道了與十進制的對應關係,已經能夠與平常計數換算了,那麼十六進制又是什麼鬼,爲何還要弄出來一個十六進制呢?

  首先咱們看二進制的寫法,一個字節要寫8次,顯然很不方便,也很差讀,口算成十進制數有很大難度。那麼十進制標準書寫是兩個字符表示一個字節,等寬制的,便於編輯瀏覽。更重要的是與二進制的換算恰好將8位分紅兩部分,每4位對應一個字符,兩個字符拼在一塊兒表明了完整的8位。下面咱們經過數據看一下對應關係:

0000 1010       //16進製爲0A,10進製爲10
0000 1111       //16進製爲0F,10進製爲15
0001 0000       //16進製爲10,10進製爲16
1000 0010       //16進製爲82,10進製爲130
1111 1111       //16進製爲FF,10進製爲255

  十六進制,顧名思義應是逢16進1纔對,但是阿拉伯數字只有0-9,那麼逢16進1至少要能表示到15才能夠。所以十六進制在0-9的基礎上又增長了A-F這6個字母,分別表明10-15。對應10進制值,00表示0,09表示9,0A表示10,0F表示15,10表示16(由於滿16就進位了)。上圖中把一個字節的8個位四四分開,分別對應先後兩個16進制字符,就很快能夠換算出來。

  第一組前四位都是0,因此十六進制第一個字符寫0;後四位是1010,是十進制的10,用十六進制應該是A,因此第二個字符應該是A。這樣合起來的十六進制值就是0A。
  第二組前四位都是0,因此十六進制第一個字符寫0;後四位是1111,是十進制的15,用十六進制應該是F,因此第二個字符應該是F。這樣合起來的十六進制值就是0F。
  第三組前四位是0001,十六進制值應該是1,因此十六進制第一個字符寫1;後四位都是0,用十六進制應該是0,因此第二個字符應該是0。這樣合起來的十六進制值就是10。
  第四組前四位是1000,十六進制值應該是8,因此十六進制第一個字符寫8;後四位是0010,用十六進制應該是2,因此第二個字符應該是2。這樣合起來的十六進制值就是82。
  第四組前四位是1111,十六進制值應該是F,因此十六進制第一個字符寫F;後四位是1111,用十六進制應該是F,因此第二個字符應該是F。這樣合起來的十六進制值就是FF,也就是十進制的255,單字節的最大值。

  十六進制在不一樣的編程語言裏面,標識符(用於標註聲明這是16進制的數字)有所不一樣,在C語言裏用「0x」標識,以下面代碼:

// C語言代碼
unsigned char a = 0xB3;         //變量a等於十進制的179
unsigned char b = 0x0C;         //變量b等於十進制的12

  關於十六進制字面量的表示方法請查閱各語言的官方手冊,這裏再也不 一一列舉。

  關於數據值的溝通

  在咱們最初的物聯網開發團隊中,嵌入式團隊與服務端團隊溝通時,常常出現這樣的發問「你那邊是發送的16進制的數據仍是10進制的數據啊」,或相似「你是以16進制形式發送的仍是以10進制形式發送的啊」、「你用16進制發的,我也得轉成16進制啊,能不能你改爲10進制發送啊」等等。

  爲何會有上面的問題,上面的問題應該這樣問嗎?後來我發現從互聯網開發轉過來的程序員多數會有這個問題,這個問題是不該該這樣問的,究其緣由主要有一下幾種:
  一、嵌入式開發人員涉及到數據的底層操做和編輯,多數都是習慣用16進制描述數據,數據編輯器也多數都是16進制的,不多有10進制的。因此溝通的時候,16進制的數值描述首先從嵌入式人員說出來了。
  二、因爲上面緣由,嵌入式的文檔中描述協議標誌的時候,也是用16進制進行定義的,這方便數據編輯(後面會討論到),因此在討論文檔的時候也會出現16進制的描述。
  三、互聯網開發人員多數習慣了10進制的數值描述,對進制轉換和字節的本質因長期不涉及相對底層的開發,就搞不清之間的關係了。
  四、最重要的是問的人對數據傳輸的字節底層含義已經淡忘了,因此一時不能適應了。

  其實在溝通中,發送方只要告訴接收方發送的是哪一個進制的多少值就能夠了。接收方用16進制讀取仍是10進制讀取決定與本身,由於數據自己就是那個二進制沒變。你讀的進制不一樣,值的面量就不一樣了,根據須要本身選擇換算就能夠了。例以下面的代碼:

// C語言代碼
unsigned char a = 0x0B;         //16進制賦值
unsigned char b = 11;           //10進制賦值

if(a == b){
    printf('OK');
}else{
    printf('NO');
}

  上面代碼執行後,控制檯輸出的是「OK」,由於十六進制的 0x0B 和十進制的 11 的二進制是同樣的。

  本節完,待續......

相關文章
相關標籤/搜索