複製鏈接
請複製以下鏈接發送給好友

字節順序

鎖定
字節順序是指佔內存多於一個字節類型的數據在內存中的存放順序,通常有小端、大端兩種字節順序。小端字節序指低字節數據存放在內存低地址處,高字節數據存放在內存高地址處;大端字節序是高字節數據存放在低地址處,低字節數據存放在高地址處。
中文名
字節順序
外文名
endian
概    念
佔內存多於一個字節類型存放順序
通常有
小端、大端
基    於
X86平台的PC機是小端字節序

目錄

字節順序簡介

基於X86平台的PC機是小端字節序的,而有的嵌入式平台則是大端字節序的。因而對int、uint16、uint32等多於1字節類型的數據,在這些嵌入式平台上應該變換其存儲順序。通常我們認為,在空中傳輸的字節的順序即網絡字節序為標準順序,考慮到與協議的一致以及與同類其它平台產品的互通,在程序中發數據包時,將主機字節序轉換為網絡字節序,收數據包處將網絡字節序轉換為主機字節序

字節順序跨平台

在本LINUX的書裏介紹到INTEL的CPU使用的小端字節序其他比MOTOROLA
68000系列CPU使用的是大端字節序如果不轉換 將數據通過網絡發出時 比如MOTOROLA發一個16位數據:0X1234 傳送到INTEL時
就被INTEL解釋為0X3412 也就是4660成了13330 所以有時候需要一些函數來進行大小端字節序的轉換

字節順序概念

關於這大小字節序的概念不是很想的明白 [1]  數據在
內存裏是具體怎麼存放的形式?為什麼會有CPU解釋的不同?數據不是按12345678……這樣的順序一直排列的麼?
如: 一個多字節值 0xFECDBA98,內存從地址100開始存放
降序: FE 對應地址100 | CD 對應地址 101 | BA 對應地址 102 | 98 對應地址 103
升序: 98 | BA | CD | FE ---->;same above
注意,我們的書寫字數據表示法是從高字節位--->低字節位(從左到右)
內存地址生長方向為: 從左到右 由低到高(這是不變的)
數據為: 0x98BADCFE
降序(Little-endian)大端字節序存儲時 由左到右
升序(Big-endian)小端字節序存儲時 由右向左

字節順序驗證

可以自己編一個小程序驗證一下(用C的數組)
更簡單的調用VC裏的checkEndian
Intel處理器的字節順序是和DEC VAX處理器的字節順序一致的。因此它與68000型處理器以及Internet的順序是不同的,所以用户在使用時要特別小心以保證正確的順序。
任何從Windows Sockets函數對IP地址端口號的引用和傳送給Windows Sockets函數的IP地址和端口號均是按照網絡順序組織的,這也包括了sockaddr_in結構這一數據類型中的IP地址域和端口域(但不包括sin_family域)。

字節順序案例1

如何手算主機字節順序轉換為網絡字節順序?
假設某16位的整數,主機字節順序的值是21,那麼它的網絡字節順序是多少?
解決的步驟是:
1、將21化成二進制,二進制,如果不足16位就在其前面補0,補滿16位。
21轉換成二進制是:10101,在它前面補0,補滿16位後就得到:
00000000#00010101
2、將這個16位二進制字符平分成兩段,每段8位
0000000 000010101 == > 00000000#00010101
3、顛倒這兩段的順序,然後去掉第一個字符“1”前面的0,化成十進制就得到了網絡字節順序的值了。
00000000#00010101顛倒後:00010101#00000000
即00 01 01 01 00 000000
去掉第一個“1”前面的0得到:10 10 10 00 00 00 0
化成十進制得到:5376
通過這個程序可以進行驗證:
#include<stdio.h>
#include<string.h>
#include<netdb.h>
intmain(
void)
{
structservent*server=NULL;
charsername[16]="\0";
charproname[4]="\0";

printf("請輸入服務名稱:");

gets(sername);
printf("請輸入使用的協議:");
gets(proname);
//如果能夠通過getserbyname函數找到結果,就輸出結果,否則報錯
if((server=getservbyname(sername,proname))!=NULL)
{

printf("
網絡服務端口:\n");
printf("網絡字節順序值:%d\n",server->s_port);
printf("主機字節順序值:%d\n",ntohs(server->s_port));
}
else
{
printf("該服務不存在\n");
}
return0;
}

編譯後,按提示輸入ftp和tcp,就得到網絡字節順序值是5376,主機字節順序值是21。
16位的轉換函數:ntohs和htons
ntohs(network to host short)是將網絡字節順序轉換為主機字節順序,返回值是一個16位的整數,即2個字節長度的整數(1字節=8位)short int,也可以寫作uint16_t。
htons(host to network short)是將主機字節順序轉換為網絡字節順序,返回值也是一個16位的整數short int。
32位轉換函數ntohl和htonl。
ntohl(network to host long)是將網絡字節順序轉換為主機字節順序,返回值是一個16位的整數,即2個字節長度的整數(1字節=8位)long int,也可以寫作uint32_t。
htonl(host to network long)是將主機字節順序轉換為網絡字節順序,返回值也是一個16位的整數long int。
IP地址使用的是32位的無符號整數,所以,在對IP地址進行處理的時候,就需要用到32位的轉換了。
一個點分十進制的IP地址是192.168.0.1,還原成原來的二進制原碼是:
11000000 10101000 0000000 00000 0001
這是主機字符順序存儲的值。
將其按照每個字節分隔開來,即每8位分隔開來,得到:
11000000 10101000 00000000 00000001
將這4段二進制編碼進行完全顛倒,就得到了網絡字節順序:
00000001000000001010100011000000
去掉第一個字符“1”前面無效的0,得到:
10 00 00 00 01 01 01 00 01 10 00 00 0
化成十進制得到:16820416
inet_addr函數可以將一個點分十進制IP地址轉換成網絡字節順序的長整形十進制值。同樣的,inet_ntoa函數將網絡字節順序的長整形十進制數值的IP地址轉換成點分十進制。

字節順序案例2

將一個點分十進制IP地址轉換成它的二進制原碼
分析:用inet_addr函數,轉換點分十進制的IP為網絡字節順序的長整型十進制數值後,再用ntohl函數將其轉換成主機字節順序的長整型整數,然後使用一個自定義函數將這個主機字節順序的長整形整數轉換成二進制。
#include<stdio.h>
#include<sys/socket.h>
#include<netdb.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
//
十進制轉二進制函數,參數為無符號超長整形(防止溢出)
voiddec(constunsignedlonglongintx)
{
if(x/2>0)
{
dec(x/2);

printf("%d",x%2);
}
else
{
printf("%d",x);
}
}
intmain(
void)
{
charipaddr[15]="\0";
unsignedlonglonginthostaddr=0;//主機字節IP的值
printf("請輸入
IP地址:");

gets(ipaddr);
hostaddr=ntohl(
inet_addr(ipaddr));

printf("IP地址的網絡字節順序的值:%ld\n",inet_addr(ipaddr));
//調用dec函數,為了防止溢出將hostaddr
強制轉換成無符號超長整形後再轉換成
二進制
printf("二進制形式是:\n");
dec((unsignedlonglongint)hostaddr);
printf("\n");
return0;
}

參考資料