1. 程式人生 > >大端和小端(big endian little endian)

大端和小端(big endian little endian)

讀寫 pue 處理器 bsp 網絡 做的 tdi har power

一、大端和小端的問題

對於整型、長整型等數據類型,Big endian 認為第一個字節是最高位字節(按照從低地址到高地址的順序存放數據的高位字節到低位字節);而 Little endian 則相反,它認為第一個字節是最低位字節(按照從低地址到高地址的順序存放據的低位字節到高位字節)。

例如,假設從內存地址 0x0000 開始有以下數據:
0x0000 0x0001 0x0002 0x0003
0x12 0x34 0xab 0xcd
如果我們去讀取一個地址為 0x0000 的四個字節變量,若字節序為big-endian,則讀出結果為0x1234abcd;若字節序為little-endian,則讀出結果為0xcdab3412。

如果我們將0x1234abcd 寫入到以 0x0000 開始的內存中,則Little endian 和 Big endian 模式的存放結果如下:
地址 0x0000 0x0001 0x0002 0x0003
big-endian 0x12 0x34 0xab 0xcd
little-endian 0xcd 0xab 0x34 0x12

一般來說,x86 系列 CPU 都是 little-endian 的字節序,PowerPC 通常是 big-endian,網絡字節順序也是 big-endian還有的CPU 能通過跳線來設置 CPU 工作於 Little endian 還是 Big endian 模式。

對於0x12345678的存儲:

小端模式:(從低字節到高字節)
地位地址 0x78 0x56 0x34 0x12 高位地址

大端模式:(從高字節到低字節)
地位地址 0x12 0x34 0x56 0x78 高位地址

二、大端小端轉換方法

htonl() htons() 從主機字節順序轉換成網絡字節順序
ntohl() ntohs() 從網絡字節順序轉換為主機字節順序

Big-Endian轉換成Little-Endian

#define BigtoLittle16(A) ((((uint16)(A) & 0xff00) >> 8) | (((uint16)(A) & 0x00ff) << 8))
#define
BigtoLittle32(A) ((((uint32)(A) & 0xff000000) >> 24) | (((uint32)(A) & 0x00ff0000) >> 8) | \ (((uint32)(A) & 0x0000ff00) << 8) | (((uint32)(A) & 0x000000ff) << 24))

三、大端小端檢測方法

如何檢查處理器是big-endian還是little-endian?

C程序:

 int i = 1;   
    char *p = (char *)&i;   
    if(*p == 1)     
          printf("Little Endian"); 
    else
          printf("Big Endian");

大小端存儲問題,如果小端方式中(i占至少兩個字節的長度)則i所分配的內存最小地址那個字節中就存著1,其他字節是0.大端的話則1在i的最高地址字節處存放,char是一個字節,所以強制將char型量p指向i則p指向的一定是i的最低地址,那麽就可以判斷p中的值是不是1來確定是不是小端。

  聯合體union的存放順序是所有成員都從低地址開始存放,利用該特性就可以輕松地獲得了CPU對內存采用Little-endian還是Big-endian模式讀寫。

/*return 1: little-endian, return 0: big-endian*/
int checkCPUendian()
{
  union
  {
    unsigned int a;
    unsigned char b; 
  }c;
  c.a = 1;
  return (c.b == 1); 
}

實現同樣的功能,來看看Linux 操作系統中相關的源代碼是怎麽做的:

static union { char c[4]; unsigned long mylong; } endian_test = {{ l, ?, ?, b } };

#define ENDIANNESS ((char)endian_test.mylong)

Linux 的內核作者們僅僅用一個union 變量和一個簡單的宏定義就實現了一大段代碼同樣的功能!(如果ENDIANNESS=’l’表示系統為little endian,為’b’表示big endian)

四、一些筆試題目

  char *sz = "0123456789"; 
  int *p = (int*)sz; 
  printf("%x\n",*++p); 

字符‘0‘對應的十六進制是0x30,請問在x86環境下程序輸出是多少?

[email protected],那麽sz在內存的存儲為
@0 @1 @2 @3 @4 @5 @6 @7 @8 @9
0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39
當你把char*強制類型轉化成int*後,因為int占四個字節,[email protected],[email protected]@[email protected]@3,打印的時候 先進行++p操作,[email protected],[email protected]@[email protected]@7,根據上面地地址存地位,高地址存高位的解釋,那麽*p應該等於0x37363534

  int a = 0x12345678;
    char *p = (char*)(&a);
    printf("%x\n",*(p+1));

例如對於0x12345678,網絡字節順序是這樣0x12,0x34,0x56,0x78存儲的,這種方式稱為big-endian
intel處理器是0x78 0x56 0x34 0x12這樣來存儲的,稱為小尾little-endian
在x86環境下題目中的p指向0x78,加1後指向0x56

#include <stdio.h>
union
{
    int i;
    char x[2];
}a;
int main()
{
    a.x[0] = 10;
    a.x[1] = 1;
    printf("%d",a.i);
    return 0;
}

x86下輸出答案: 266 (x86下:低位低地址,高位高地址,i內存裏存的值是Ox010A,十進制為266)

int main()
{
    union
    {
        int i;
        struct
        {
            char first;
            char second;
        }half;
    }number;
    number.i=0x4241;
    printf("%c %c\n", number.half.first, number.half.second);
    number.half.first=‘a‘;
    number.half.second=‘b‘;
    printf("%x\n", number.i);
    return 0;
}

x86下輸出答案:
A B (0x41對應‘A‘,是低位;Ox42對應‘B‘,是高位)
6261 (number.i和number.half共用一塊地址空間0x6261)

大端和小端(big endian little endian)