1. 程式人生 > >大端和小端的判斷及轉換

大端和小端的判斷及轉換

當前的儲存器,多以byte為訪問的最小單元,當一個邏輯上的地址必須分割為物理上的若干單元時就存在了先放誰後放誰的問題,於是端(endian)的問題應運而生了,對於不同的儲存方法,就有大端(big-endian)和小端(little- endian)兩個描述。

位元組排序按分為大端和小端,概念如下

大端(big endian):低地址存放高有效位元組

小端(little endian):低位元組存放地有效位元組

現在主流的CPU,intel系列的是採用的little endian的格式存放資料,而motorola系列的CPU採用的是big endian,ARM則同時支援 big和little,網路程式設計中,TCP/IP統一採用大端方式傳送資料,所以有時我們也會把大端方式稱之為網路位元組序。

特別需要注意的是,C/C++語言編寫的程式裡資料儲存順序是跟編譯平臺所在的CPU相關的,而 JAVA編寫的程式則唯一採用big endian方式來儲存資料。這裡我就只討論C/C++語言的情況。

1.大端和小端的方式及判斷

舉個例子說明,我的機子是32位windows的系統,處理器是AMD的。對於一個int型數0x12345678,為方便說明,這裡採用16進製表示。這個數在不同位元組順序儲存的CPU中儲存順序如下:

0x12345678   16進位制,兩個數就是一位元組

高有效位元組——>低有效位元組: 12 34 56 78

          低地址位     高低址位

大端

:  12  34        56   78

小端: 78  56        34   12

下面驗證下本機CPU屬於哪種位元組儲存順序。程式碼如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

#include <iostream>

using namespace std;

typedef unsigned

int UINT;

typedef unsigned char UCHAR;

int main()

{

UINT i=0x12345678;

cout<<hex<<i<<endl;

UCHAR *p = (UCHAR*)&i;          //將i的地址傳給陣列指標p,實際上p指向的地址是i在記憶體中儲存的第一個位元組,大端就是0x12,小端就是0x78

if((*p==0x78)&(*(p+1)==0x56))       

cout<<"小端"<<endl;

else if((*p==0x12)&(*(p+1)==0x34))

cout<<"大端"<<endl;

else

cout<<"這是神馬位元組順序呢?";

return 0;

}

除錯顯示時小端,我用的機子位元組儲存為小端方式。

2.大端和小端的位元組轉換

當兩臺採用不同位元組序的主機通訊時,在傳送資料之前都必須經過位元組序的轉換成為網路位元組序(即大端方式)後再進行傳輸。此外用C/C++在小端方式的機器上編寫的程式與java程式互通時也要進行大端和小端的轉換。

這裡所謂轉換就是改變位元組的排序,使互動時資料保持一致。舉一個例子,還是16進製表示的數0x12345678,在小端機器上排序為0x78563412,當記憶體中這樣的數傳輸時,在大端方式下就是0x78563412這個值,與原值不同,要想與原值相同,在傳輸前,在大端方式下就該是0x12345678,這時原數在記憶體中為0x12345678,即將原資料0x12345678在記憶體儲存序列為0x12345678,也就是要轉換成大端方式。

要傳輸值:12 34 56 78

不轉換時,小端:78 56 34 12

轉換為大端:12 34 56 78

根據上面的大端和小端位元組排序,可以方便的用移位運算完成轉換功能。從小端轉到大端程式碼如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

#include <iostream>

using namespace std;

typedef unsigned int UINT;

typedef unsigned char UCHAR;

int main()

{

UINT i=0x12345678;

cout<<hex<<i<<endl;

UCHAR *p = (UCHAR*)&i;

UINT num,num1,num2,num3,num4;

num1=(UINT)(*p)<<24;

num2=((UINT)*(p+1))<<16;

num3=((UINT)*(p+2))<<8;

num4=((UINT)*(p+3));

num=num1+num2+num3+num4;

cout<<"num1:"<<hex<<num1<<endl;     //看num1的16進製表示,下同

cout<<"num2:"<<hex<<num2<<endl;

cout<<"num3:"<<hex<<num3<<endl;

cout<<"num4:"<<hex<<num4<<endl;

cout<<"num:"<<hex<<num<<endl;

unsigned char *q = (unsigned char*)&num;

if((*q==0x78)&(*(q+1)==0x56))         

cout<<"小端"<<endl;

else if((*q==0x12)&(*(q+1)==0x34))

cout<<"大端"<<endl;

else

cout<<"這是神馬位元組順序呢?";

return 0;

}

至於說(UINT)(*p)為什麼要移24位,其實是很好理解的,將0x00000012變成0x12000000,不就是向左移24位嗎。

當然,向上面這樣寫時為了方便理解,可以更簡單的寫一個函式用於完成上面的轉換功能,函式如下:

1

2

3

4

5

UINT EndianConvertLToB(UINT InputNum) {

UCHAR *p = (UCHAR*)&InputNum;

return(((UINT)*p<<24)+((UINT)*(p+1)<<16)+

((UINT)*(p+2)<<8)+(UINT)*(p+3));

}

同樣的原理適用於大端轉小端,但是大端轉小端時移位有差別,函式如下:

1

2

3

4

5

UINT EndianConvertBToL(UINT InputNum) {

UCHAR *p = (UCHAR*)&InputNum;

return(((UINT)*p)+((UINT)*(p+1)<<8)+

((UINT)*(p+2)<<16)+(UINT)*(p+3)<<24);

}

分類: C/C++

標籤: 大端和小端轉換判斷