字节序(剖析)

一、引言

在对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;
}

如有错误及理解不到位的地方敬请指出,谢谢阅读。

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