字節序(剖析)

一、引言

在對seg-y格式的地震數據進行讀取操作時發現,對文件頭及道頭中指定位置進行讀取時,得到的參數並不是自己想要的,而是一個奇怪的數字,查閱資料發現是字節序的問題,存儲時使用的是小端模式的字節序,讀取時按照了大端模式的字節序進行讀取,因此導致了錯誤,經過近兩天的探索研究,對字節序有了一定的理解,特此記錄下來。

二、基本概念及名詞辨析

1. 高字節位與低字節位

舉個例子:對於一個16進制數,0x10203040,按照常規數字寫法,左邊爲高位,右邊爲低位,這裏同理,高字節位就是10,低字節位就是40,當然這裏是相對而言,記住左高右低的原則即可。

2. 高地址和低地址

談到了地址,不可避免的要提及內存,將一個數據儲存在內存中,按照地址來分配內存區域,有一定的規則,地址編號小的稱爲低地址,地址編號大的稱爲高地址,地址的增長方向與內存生長方向有關,如下圖所示
內存模型圖
舉個例子:0x0806在內存中就是按照下面這種方式存儲的,這裏採用的字節序是大端模式。

棧頂底(高地址)
0x06 -- 低位 
0x08 -- 高位
棧頂 (低地址)

3.大端模式和小端模式

仍以0x0806在內存中的存儲方式爲例:

3.1 大端模式

棧頂底(高地址)
0x06 – 低位
0x08 – 高位
棧頂 (低地址)

從這個存儲方式我們可以發現大端模式字節序的規律,字節位和地址的對應關係是:高字節位-低地址,低字節位-高地址。假設內存生長方向是從低地址向高地址生長,那麼在低地址先存高字節位的數據,生長到高地址則存儲低字節的數據。

3.2 小端模式

棧頂底(高地址)
0x08 – 高位
0x06 – 低位
棧頂 (低地址)

小端模式字節序的規律,字節位和地址的對應關係是:高字節位-高地址,低字節位-低地址。小端模式的好處體現在讀取時更加便利,因爲讀取時是從低地址開始讀取,這樣讀取速度更快。

3.3 小結

大小端字節序是由計算設備的cpu決定的,與系統無關。
檢測本機是哪種模式的字節序可以通過一個小程序判斷,觀察高低字節位和高低地址的對應關係,可以判斷本機是大端還是小端模式的字節序。

#include<stdio.h>
int main()
{
    int i = 0x10203040;
    char *p = (char *)(&i);
    printf("內存地址 :%x  %x\n",p,*p);
    printf("內存地址 :%x  %x\n",p+1,*(p+1));
    printf("內存地址 :%x  %x\n",p+2,*(p+2));
    printf("內存地址 :%x  %x\n",p+3,*(p+3));
}

我的結果是:小端模式字節序,低字節位對應低地址,我的cpu是inter COREi7,inter的基本都是小端模式

內存地址 :65fe14  40
內存地址 :65fe15  30
內存地址 :65fe16  20
內存地址 :65fe17  10

三、如何讀取不同字節序存儲模式的數據

明白了存儲方式之後,就可以針對不同的字節序存儲方式進行讀取。
例如,我們要在一個short int中以小端模式字節序存儲一個值1,
真值:1
二進制碼:0000 0000 0000 0001
|----高位----|----低位----|
小端存儲:|–低地址–|--高地址–|
|----低位----|----高位----|
0000 0001 0000 0000
寫成16進制即爲:0x0100;
然而讀取時,0x0100=11616+0=256,此時出現的就是大小端字節序存取不一致的問題。
可以看出存儲模式是小端存儲,但是實際上值卻是按大端模式計算,因爲習慣上大家都是按照高位在前地位在後進行計算的,因此,小端模式存儲的值需要經過處理才能恢復原值。
對於不同類型的整數需要使用不同的方式恢復原值。

1. short int類型在小端字節序模式存儲下的恢復

#define BLEndianshort(x)(value=((x& 0x00FF) << 8 ) | ((x& 0xFF00) >> 8));

推薦使用宏定義方式進行轉換,當然也可以使用函數方式。耗費內存資源多少的問題。

2. long int類型在小端字節序模式存儲下的恢復

#define BLEndianlong(x) (x=((x & 0x000000FF) << 24) | ((x & 0x0000FF00) << 8) | ((x & 0x00FF0000) >> 8) | ((x & 0xFF000000) >> 24));   

3. float類型在小端字節序模式存儲下的恢復

這裏使用了union聯合體及函數對float類型的數據進行恢復。

typedef union FLOAT_BLSWAP
{
    float f;
    char c[4];
}float_blswap;
float BLEndianFloat(float x)
{
    float_blswap d1,d2;
    d1.f = x;
    d2.c[0] = d1.c[3];
    d2.c[1] = d1.c[2];
    d2.c[2] = d1.c[1];
    d2.c[3] = d1.c[0];
    return d2.f;
}

如有錯誤及理解不到位的地方敬請指出,謝謝閱讀。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章