在談通訊相關編程的時候,我們需要對存儲格式和機制加以留意。
1、由來
1980年,Danny Cohen在其著名的論文"On Holy Wars and a Plea for Peace"中為了平息一場關於在消息中字節該以什么樣的順序進行傳送的爭論而引用了該詞。該文中,Cohen非常形象貼切地把支持從一個消息序列的最高位開始傳送的那伙人叫做Big-Endians,支持從最低位開始傳送的相對應地叫做Little-Endians。此后Endian這個詞便隨着這篇論文而被廣為采用。(摘自百度百科Endian詞條)
2、術語
小端 = Little-Endians = Intel Mode:高字節保存在高地址中,低字節保存在低地址中。按我們書寫的方向,低字節在右,高字節在左。
大端 = Big-Endians = Motorola Mode:高字節保存在低地址中,低字節保存在高地址中。按我們書寫的方向,高字節在右,低字節在左。
3、說明
在一個8位的字節中,二進制數以高位在左,低位在右排列,如同我們書寫十進制數的格式。
在表達一個多字節的整數時,就出現大端和小端的分歧,這主要與CPU的架構有關。Intel系列和ARM系列均采用小端,KEIL C51系列均采用大端。
以CAN標准的Data數據排列為例:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 |
將上述數據以流的方式讀入計算機內存的字節數組對應:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 |
不過,需要說明的這只是CAN的一幀數據的Data部分,包含了多個信號數據,這些信號數據的起始位,位長和格式(采用小端或大端)都可以單獨定義。
4、應用協議
在進行計算機通訊編程時,行走於網絡中的數據來自不同的設備,根據通訊協議,我們需要對其進行轉換。
比較多的工業傳輸協議,例如串口協議,CANOPEN和MODBUS協議等。廣泛用於汽車,傳感器和控制的數據傳輸方面。
在CAN數據結構中,CANID是固定的小端結構,而8個字節的Data部分則是自定義,協議記錄在BDC文件中。
5、轉換方法
以下為Delphi代碼,但是使用的函數均為Windows的API函數。
方法一,使用Windows.unit的makeword和makelong函數
type Tbyte4: array [0..3] of byte; function Convert(Value: longword): longword; implementation function Convert(Value: longword): longword; var myData: ^Tbyte4; begin myData1 := @Value; Result := makelong(makeword(myData^[3], myData^[2]), makeword(myData^[1], myData^[0])); end;
方法二,使用Winsock.unit的htons和htonl函數,這是最經典的代碼
function Convert(Value: longword): longword; begin Result := htonl(Value); end;
function Convert(Value: word): word; begin Result := htons(Value); end;
這里,htons()函數是將一個無符號的短整形數(16位)值轉換成網絡字節順序,其實就是將小端轉成大端。同理htonl()用於無符號長整形(32位)。
需要注意的是,htons()用於將主機短整數轉換成網絡字節順序,反過來,則使用ntohs()函數。htonl()則對應ntohl()。
方法三,了解上述原理后,不妨自己用指針寫個更加高效的程序。