自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

我們一起聊聊如何理解字節(jié)序

開發(fā) 前端
字節(jié)序是計算機(jī)存儲多字節(jié)數(shù)據(jù)的方式,目前的方式有:大端字節(jié)序和小端字節(jié)序,字節(jié)序主要是針對多字節(jié)的數(shù)據(jù)類型,比如 short、int 等。

 [[439739]]

計算機(jī)只能理解 0 和 1 組成的二進(jìn)制數(shù)據(jù), 一個 bit 的值是 0 或 1,八個這樣的 bit 組成了一個字節(jié),通過字節(jié),計算機(jī)可以表示一些復(fù)雜的數(shù)據(jù),比如:音頻、視頻等,有些數(shù)據(jù)用一個字節(jié)就能表示,比如英文字符,而有些數(shù)據(jù)需要多個字節(jié)來表示,比如:漢字, 對于多字節(jié)的數(shù)據(jù),存儲的時候會有字節(jié)順序的問題,也就是字節(jié)序

字節(jié)序是什么

字節(jié)序是計算機(jī)存儲多字節(jié)數(shù)據(jù)的方式,目前的方式有:大端字節(jié)序和小端字節(jié)序,字節(jié)序主要是針對多字節(jié)的數(shù)據(jù)類型,比如 short、int 等

  • 大端字節(jié)序

高位字節(jié)存儲在內(nèi)存的低地址上,低位字節(jié)存儲在內(nèi)存的高位地址上

  • 小端字節(jié)序

高位字節(jié)存儲在內(nèi)存的高地址上,低位字節(jié)存儲在內(nèi)存的低地址上

如何理解字節(jié)序

我們平常書寫和閱讀數(shù)字的習(xí)慣是從左到右的,所以把最左邊的字節(jié)當(dāng)作最高位字節(jié),最右邊的字節(jié)當(dāng)作做最低位字節(jié),從左到右,表示從高位字節(jié)到低位字節(jié)

例如:對于 0x01020304,它的大端和小端字節(jié)序在內(nèi)存中的布局如下圖所示

0x 01 02 03 04 總共四個字節(jié)大小,以人們習(xí)慣的閱讀順序,0x01 處于左邊,屬于高位字節(jié),0x04 處于右邊,屬于低位字節(jié)

內(nèi)存地址從 0x 00 00 00 07 到 0x 00 00 00 0A 4個字節(jié)的空間,剛好能存儲得下

根據(jù)大端字節(jié)序的的規(guī)則:高位字節(jié)存儲在內(nèi)存低地址,所以處于高位字節(jié)的 0x01 存儲在 0x 00 00 00 07 地址處,緊接著 次高位字節(jié) 0x02 存儲在次低地址 0x 00 00 00 08 處,剩下的兩個字節(jié) 0x03 和 0x04 分別存儲于 0x 00 00 00 09 和 0x 00 00 00 0A 地址處,最后的結(jié)果是 0x 01 02 03 04

小端字節(jié)序和大端剛好相反,它指的是 高位字節(jié)存儲在內(nèi)存高地址處,所以處于高位字節(jié)的 0x01 存儲在 0x 00 00 00 0A 地址處,次高位字節(jié) 0x02 存儲在次高地址 0x 00 00 00 09 處,余下的 0x03 和 0x04 分別存儲于 0x 00 00 00 08 和 0x 00 00 00 07 地址處,最后的結(jié)果是 0x 04 03 02 01

從上圖可以看出,對于相同的數(shù)據(jù),大端和小端的內(nèi)存布局是不一樣的,大端字節(jié)序的存儲形式更符合人們平常書寫和閱讀的習(xí)慣

為什么會有字節(jié)序

可能有人會感到疑惑:既然大端字節(jié)序更符合人們閱讀的習(xí)慣,為什么不全部都采用大端的方式,這樣也就不會有字節(jié)序的問題了 ?

確實,如果所有平臺都用同一種存儲順序,就沒有字節(jié)序這一說法了

在早期, CPU 只有幾千個邏輯門,小端的方式能更有效的使用邏輯電路,所以很多計算機(jī)內(nèi)部計算都采用小端的方式,這種方式也就保留到了現(xiàn)在

另外,字節(jié)序是跟 CPU 架構(gòu)相關(guān),不同的廠家設(shè)計的規(guī)范可能都不一樣,比如 Intel 的 x86 是小端方式,而 IBM 的 PowerPC 則采用大端方

大端的方式更符合人們的閱讀習(xí)慣,因此大部分網(wǎng)絡(luò)傳輸以及文件存儲都是大端的方式

總的來說,小端主要是在計算機(jī)內(nèi)部使用,大端則在外部使用

計算機(jī)如何處理字節(jié)序

計算機(jī)讀取數(shù)據(jù)的時候是不區(qū)分字節(jié)序的,它總是從內(nèi)存低地址到高地址的順序,按字節(jié)讀取

下面的示例圖展示了數(shù)據(jù) 0x0102 的 大端和小端的內(nèi)存布局以及CPU讀取內(nèi)存的順序

由上圖可知,對于大端字節(jié)序來說,內(nèi)存低地址處存儲的是高位字節(jié),也即計算機(jī)讀取內(nèi)存的第一個字節(jié)就是高位字節(jié),小端字節(jié)序就正好相反,內(nèi)存低地址處存儲的是低位字節(jié),讀取內(nèi)存的第一個字節(jié)是低位字節(jié)

計算機(jī)只有在讀取數(shù)據(jù)的時候才需要區(qū)分字節(jié)序

就拿上面展示大端方式的圖 ( 第一張 ) 來說,內(nèi)存 0x 00 00 00 07 地址處存儲的數(shù)據(jù)是 0x01 , 0x 00 00 00 08 地址處存儲的數(shù)據(jù)是 0x02

如果是以大端的方式讀取的話,地址 0x 00 00 00 07 處的數(shù)據(jù) 0x01 會放到高位字節(jié), 0x 00 00 00 08 處的數(shù)據(jù)是 0x02 放到低位字節(jié),最終這兩個字節(jié)的數(shù)據(jù)是 0x 01 02

如果是以小端的方式讀取的話,,地址 0x 00 00 00 07 處的數(shù)據(jù) 0x01 會放到低位字節(jié), 0x 00 00 00 08 處的數(shù)據(jù)是 0x02 放到高位字節(jié),最終這兩個字節(jié)的數(shù)據(jù)是 0x 02 01

網(wǎng)絡(luò)字節(jié)序

所有的協(xié)議都是人類編制定的,大端對人們閱讀更友好,所以 IEEE 標(biāo)準(zhǔn)協(xié)會規(guī)定除非有明確說明,否則網(wǎng)絡(luò)協(xié)議都使用大端字節(jié)序, 像 TCP/IP 就是如此

還記得我們在編寫網(wǎng)絡(luò)程序的時候,傳入 connect 函數(shù)實參中的 端口號嗎, 傳入之前需調(diào)用 htons 函數(shù)將其轉(zhuǎn)成網(wǎng)絡(luò)字節(jié)序,也就是要轉(zhuǎn)成大端字節(jié)序,下面是部分代碼示例

  1. struct sockaddr_in addr; 
  2.  
  3. addr.sin_family = AF_INET; 
  4.  
  5. addr.sin_addr.s_addr = inet_addr("192.168.1.10"); 
  6.  
  7. addr.sin_port = htons( 5000 ); 
  8.  
  9. connect( clientfd, (struct sockaddr *)&addr, sizeof(addr)) ) 

上面紅色的 htons 函數(shù)的作用是將 端口號 由主機(jī)字節(jié)序轉(zhuǎn)成網(wǎng)絡(luò)字節(jié)序,網(wǎng)絡(luò)字節(jié)序大多時候都是固定為大端序的,但不同的機(jī)器,主機(jī)序卻不一樣,如果本身就已經(jīng)是大端了,調(diào)用 htons 函數(shù),返回值和實參是一樣的,如果本身是小端,結(jié)果會轉(zhuǎn)成大端的形式,具體的數(shù)值也會不一樣

怎么判斷大小端

上面提到了主機(jī)字節(jié)序,那如何知道當(dāng)前機(jī)器是大端還是小端呢 ?

因為操作系統(tǒng)必須適配所有類型的 CPU ,所以對于操作系統(tǒng)來說,大端和小端它都是支持的

為了讓程序易于判斷當(dāng)前平臺是大端還是小端,Linux 下 glibc 庫提供了下面幾個宏定義

  1. BIG_ENDIAN # 大端序 
  2.  
  3. LITTLE_ENDIAN # 小端序 
  4.  
  5. BYTE_ORDER # 字節(jié)序 

下面是測試代碼 test.c 文件

  1. #include <stdio.h> 
  2. int main(int argc, char *argv[]) 
  3.     if(BYTE_ORDER == BIG_ENDIAN) 
  4.     { 
  5.         printf("big endian...\n"); 
  6.     } 
  7.     else 
  8.     { 
  9.         printf("little endian...\n"); 
  10.     } 

執(zhí)行 gcc -g -o test test.c 命令進(jìn)行編譯,運行測試程序,結(jié)果如下:

  1. [root@localhost test]# ./test 
  2.  
  3. little endian... 

由此,可以知道當(dāng)前平臺是小端字節(jié)序

除了用上面的方法之外,我們可以根據(jù)大端和小端的特點,自己寫代碼獲取,修改 test.c 文件,內(nèi)容如下

  1. #include <stdio.h> 
  2. int main(int argc, char *argv[]) 
  3.     union 
  4.     { 
  5.         unsigned short i; 
  6.         char ch[2]; 
  7.     }un; 
  8.      
  9.     un.i = 0x0102; 
  10.     if(0x01 == un.ch[0]) 
  11.     { 
  12.          printf("big endian...\n"); 
  13.     } 
  14.     else 
  15.     { 
  16.          printf("little endian...\n"); 
  17.     } 

編譯并運行,結(jié)果如下:

  1. [root@localhost test]# ./test 
  2.  
  3. little endian... 

可以看出,不管是通過系統(tǒng)庫提供的宏來判斷還是自行封裝接口來判斷機(jī)器的字節(jié)序都是可行的

最后,如果想知道 LITTLE_ENDIAN、 BIG_ENDIAN 、BYTE_ORDER 宏定義的詳細(xì)情況,可以查看 glibc 源碼,它們在 glibc-2.17\string\endian.h 以及 glibc-2.17\sysdeps\x86\bits\endian.h 文件中

注意:不同版本的 glibc 源碼,具體的位置可能有差異,我使用的是 glibc-2.17 版本

大端小端的轉(zhuǎn)換

熟悉了大端和小端特點,它們之間的轉(zhuǎn)換就簡單了,對于兩字節(jié)來說,每個字節(jié)值不變,互換字節(jié)位置,如果是更多字節(jié)的話,最低位字節(jié)和最高位字節(jié)交換,次低位字節(jié)與次高位字節(jié)交換,直到所有字節(jié)都完成了一遍交換為止

比如:下面是小端轉(zhuǎn)大端的偽代碼

  1. #小端轉(zhuǎn)大端  假設(shè):ch 和 i  是小端序 
  2.  
  3. char ch[2]; 
  4.  
  5. int i = 0; 
  6.  
  7. # x 是大端字節(jié)序 
  8. x = ch[1] << 8 | ch[0]  
  9.  
  10. # y 是大端字節(jié)序 
  11. y =   ( (i & 0xff000000) >> 24 ) |  ( (i & 0x00ff0000) >> 8 ) | ( (i & 0x0000ff00) << 8 )  | ( (i & 0x000000ff) << 24 ) 

變量 i 字節(jié)序轉(zhuǎn)換說明:按照從左到右的順序,把 i 的第一個字節(jié)右移 3 個字節(jié)( 24 bit ),第二個字節(jié)右移 1 字節(jié) ( 8 bit ),第三個字節(jié)左移 1 字節(jié) ( 8 bit ),第四個字節(jié)左移 3 個字節(jié) ( 24 bit ),最后把移位后的字節(jié)組合起來就可以了

在實際的程序處理中,不應(yīng)該出現(xiàn)字節(jié)序的問題,只有 "網(wǎng)絡(luò)字節(jié)序" 和 "主機(jī)字節(jié)序" ,需要轉(zhuǎn)換字節(jié)序時,使用 ntohl, ntohs, htonl, htons 等函數(shù)即可

  1. ntohl       # uint32 類型 網(wǎng)絡(luò)序轉(zhuǎn)主機(jī)序 
  2. htonl       # uint32 類型 主機(jī)序轉(zhuǎn)網(wǎng)絡(luò)序 
  3.  
  4. ntohs       # uint16 類型 網(wǎng)絡(luò)序轉(zhuǎn)主機(jī)序 
  5. htons       # uint16 類型 主機(jī)序轉(zhuǎn)網(wǎng)絡(luò)序 

小結(jié)

本文詳述了字節(jié)序的一些知識,開發(fā)網(wǎng)絡(luò)應(yīng)用的時候會涉及到字節(jié)序的相關(guān)問題,所以,花點兒時間弄明白還是很有必要的

 

責(zé)任編輯:武曉燕 來源: Linux開發(fā)那些事兒
相關(guān)推薦

2023-08-04 08:20:56

DockerfileDocker工具

2022-05-24 08:21:16

數(shù)據(jù)安全API

2023-08-10 08:28:46

網(wǎng)絡(luò)編程通信

2023-09-10 21:42:31

2023-06-30 08:18:51

敏捷開發(fā)模式

2021-08-27 07:06:10

IOJava抽象

2024-02-20 21:34:16

循環(huán)GolangGo

2021-10-26 09:55:52

CAP理論分布式

2023-04-03 00:09:13

2024-09-09 00:00:00

編寫技術(shù)文檔

2024-09-30 09:33:31

2024-11-27 16:07:45

2022-02-23 08:41:58

NATIPv4IPv6

2022-09-22 08:06:29

計算機(jī)平板微信

2024-07-26 09:47:28

2021-08-12 07:49:24

mysql

2023-03-26 23:47:32

Go內(nèi)存模型

2024-11-28 09:57:50

C#事件發(fā)布器

2022-10-08 00:00:05

SQL機(jī)制結(jié)構(gòu)

2023-07-24 09:41:08

自動駕駛技術(shù)交通
點贊
收藏

51CTO技術(shù)棧公眾號