一、概念

  1. 计算机系统中内存是以字节为单位进行编址的,每个地址单元都唯一的对应着 1 个字节(8 bit)

  2. 有些类型的长度是超过 1 个字节的,比如 C/C++ 中,short 类型一般是 2 个字节,int 类型一般 4 个字节等。因此这里就存在如何安排多字节数据中,各字节存放顺序的问题。正是因为不同的安排顺序导致了大端存储模式和小端存储模式的存在。

    • 大端模式:是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中(高低低高)
    • 小端模式:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中(高高低低)
  3. 假如有一个 4 字节的数据为 0x12345678(十进制:305419896,0x12 为高字节,0x78 为低字节),若将其存放于地址 0x4000 8000 中,则有:

    内存地址 0x4000 8000(低地址) 0x4000 8001 0x4000 8002 0x4000 8003(高地址)
    大端模式 0x12 0x34 0x56 0x78
    小端模式 0x78 0x56 0x34 0x12
  4. 现状:

    • Intel 的 80×86 系列芯片使用小端存储模式
    • ARM 芯片默认采用小端,但可以切换为大端
    • MIPS 芯片采用大端,但可以在大小端之间切换
    • 在网络上传输的数据普遍采用的都是大端

二、判断字节序

  • 不同的 CPU 有不同的字节序类型,这些字节序是指整数在内存中保存的顺序,也叫做主机序
  • 内存的读写永远从低地址开始读/写(起始地址),从低到高
  • 对一个对象取地址,这个指针是对象使用的最低字节地址(对象的起始地址)
  • 大端模式(Big endian):将高序字节存储在起始地址
  • 小端模式(Little endian):将低序字节存储在起始地址
  1. 通过将多字节数据强制类型转换成单字节数据,再通过判断起始存储位置是数据高字节还是低字节进行检测

    int i = 1;   
    char *p = (char*)&i; // 读取 int 的第一个字节(起始地址)  
    if(*p == 1){    
        printf("Little Endian"); 
    }
    else{
        printf("Big Endian");
    }
    
  2. 利用联合体 union 的存放顺序是所有成员都从低地址开始存放这一特性进行检测

    bool isBigEndian()
    {
        union uendian
        {
            int nNum;
            char cLowAddressValue;
        };
    
        uendian u;
        u.nNum = 0x12345678;
    
        if ( u.cLowAddressValue == 0x12 )     return true;
    
        return false;
    }
    
  3. Linux 操作系统中相关的源代码

    static union { char c[4]; unsigned long mylong; } endian_test = {{ 'l', '?', '?', 'b' } };
    
    #define ENDIANNESS ((char)endian_test.mylong)
    
  4. 测试

void show_bytes(unsigned char* start, size_t len){
    for(size_t i = 0; i < len; ++i){
        printf(" %.2x", start[i]);
    }
    printf("\n");
}
void show_int(int x){
    show_bytes((unsigned char*)&x, sizeof(int));
}
void show_float(float x){
    show_bytes((unsigned char*)&x, sizeof(float));
}
void show_pointer(void* x){
    show_bytes((unsigned char*)&x, sizeof(void*));
}
void test_show_bytes(int val){
    int ival = val;
    float fval = (float)ival;
    int* pval = &ival;
    
    cout << hex << ival << endl;
    show_int(ival);
    
    cout << hex << fval << endl;
    show_float(fval);
    
    cout << hex << pval << endl;
    show_pointer(pval);
}
int main()
{
    int i = 12345;
    test_show_bytes(i);
    
    return 0;
}

输出:

3039
 39 30 00 00
12345
 00 e4 40 46
0x7fffbf791af8
 f8 1a 79 bf ff 7f 00 00

参考:

  1. 关于高位字节与低位字节简洁明了的说明,以及高低字节序转换函数
  2. 大端模式、小端模式、高字节序、低字节序、MSB、LSB
  3. 大端和小端(Big endian and Little endian)
  4. 大端小端。。。
  5. 二进制、16进制、大端小端