1. 程式人生 > >大端/小端,高字節/低字節,高地址/低地址,移位運算

大端/小端,高字節/低字節,高地址/低地址,移位運算

地址 我愛你 沒有 字段 移動 就會 strong spa 消失

其實大端小端的概念比較好理解的,大端:數據的高字節存放在內存的低地址中。

數組的聲明方式是從左往右,地址逐漸增大。

int8_t a[] = { 1, 2, 3 };                                                                       
for (int i = 0; i < 3; i++)
    printf("a[%d]: %p\n", i, &a[i]);
a[0]: 0x7ffce52cf290
a[1]: 0x7ffce52cf294
a[2]: 0x7ffce52cf298

int8_t是<stdint.h>定義的跨平臺數據類型,代表8位(1個字節)。這裏a[0]地址比a[1]地址小,a[0]就是低地址,a[1]就是高地址。

現代人的閱讀習慣都是從左向右,大端就是先看到的一邊(低地址)是“大”的(高字節),那麽什麽是高字節呢?

用十進制來舉例,小時候學數的表示,有個位、十位、百位、千位……比如一個十進制的數:1234。個位是4,十位是3,那麽低位就是4,高位是3。

計算機存儲的是二進制數,8位構成一個字節,作為基本單位。比如兩個字節的數:0x1122,化成十進制即17*256+34。低位就是34(0x22),高位就是0x11。

在書寫數字時,我們則習慣把高位的寫在前面(左邊),這和地址的書寫順序(低地址地寫在左邊)剛好相反。

於是,對於0x1122,假如計算機存儲的順序是:0x11 0x22,那麽就是大端,符合書寫習慣,高位的0x11在左邊(低地址)。

但是不是所有計算機都會按照我們習慣上這樣這麽存儲,假如計算機存儲的順序是:0x22 0x11,那麽就是小端。

判斷的方式很簡單

int16_t x = 0x1122;
int8_t* p = (int8_t*)&x;
if (p[0] == 0x11 && p[1] == 0x22)
    // 大端
int8_t a[] = { 0x11, 0x22 };
int16_t* p = (int16_t*)&a[0];
if (*p == 0x1122)
    // 大端

C的指針指向的是一個數據類型的起始字段,比如這裏用1字節的指針指向2字節的數,假設其占據的內存範圍為[p, p+2),那麽2字節數的起始地址是p,&x得到的也是p,指針之間的強制轉換不會改變指針的值(比如之前輸出的&a[0]、&a[1]、&a[2]的結果就是指針的值,也就是地址的值),只會告訴編譯器,如果要用*p讀取這個數,我們只想要這個數類型對應大小的字節。

可能說得比較繞,具體表現就是:*(&x)得到的是[p, p+2)的部分,*((int8_t)&x)得到的是[p, p+1)的部分。

在網絡傳輸中,如果沒有一個固定的存儲方式,那麽不同存儲方式的主機互相通信時就會表達失敗。比如A想告訴B:“我愛你”。結果B聽到的是“我愛你”,但是理解的卻是“你愛我”,這樣就導致了誤解。這就是區分出大端和小端的意義,在網絡傳輸中會都轉換成小端或大端的一種。

好了,大端小端的問題說完了,現在說下移位運算。我之前用下面這種做法判斷系統是大端還是小端

int16_t x = 0x1122;
if ((x >> 8) == 0x11)
    // 大端

但是這是種錯誤的做法!

移位運算是獨立於地址存儲方式的,因此x >> 8得到的是2字節數x的高字節,右移代表向低字節移動,低字節的0x22沒有更低的位置了,於是消失,高字節的0x11移動到了0x22的位置,因此x >> 8必然得到0x0011。

大端/小端,高字節/低字節,高地址/低地址,移位運算