redis - 源码 endianconv 大端、小端与网络字节序


1. 什么是大端,什么是小端:

       所谓的大端模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;

       所谓的小端模式,是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中。


     大端(Big-Endian),小端(Little-Endian)以及网络字节序的概念在编程中经常会遇到,其中网络字节序(Network Byte Order)一般是指大端(Big-Endian,对大部分网络传输协议而言)传输,大端小端的概念是面向多字节数据类型的存储方式定义的

      首先大小端是面向多字节类型定义的,比如2字节、4字节、8字节整型、长整型、浮点型等,单字节的字符串一般不用考虑

      

      假设一个32位 unsigned int型数据0x12 34 56 78,大小端8位存储方式如下:

  • 大端存储方式为0x12 34 56 78
  • 小端存储方式为0x78 56 34 12

 2 怎么查看是大端还是小端:

      写了一段C语言,int型占4个字节,而char型占1个字节,所以主函数中的语句"char* pAddress=(char*)&a;"将a的4个字节中的第一个字节地址赋给char* 型指针变量pAddress。这里pAddress用char*型而不是int*型的原因是,虽然两者都是指针,都占用4个字节,但对于所指向内容的读取方式是不同的;前者以单字节读取,而后者一次读取4个字节。

#include "stdio.h"

int main(void)
{
    printf("Hello, world!\n");
	int i = 0x12345678;
	unsigned int *p = &i;
	printf("%p \n", &i);
	
	char* pAddress=(char*)&i;  
        printf("%x\t",*pAddress);  
	printf("%x\t",*(pAddress + 1));  
	printf("%x\t",*(pAddress + 2));  
	printf("%x\t",*(pAddress + 3));  

    return 0;
}
      结果是 78 56 34 12,说明是小端模式。

$ ./hello.exe
Hello, world!
0x22cadc
78      56      34      12

3 常见CPU的大小端存储方式

      不同CPU有不同的字节序类型,典型的使用小端存储的CPU有:Intel x86和ARM

      典型的使用大端存储CPU有:Power PC、MIPS UNIX和HP-PA UNIX

      上面的测试程序在x86机器上运行的,验证了这一说法,没有其他CPU的机器,所以只验证了一种机型。

4 为什么要区分大端、小端?

      

       为什么截然相反的大小端存储模式能够并存至今?在标准化备受推崇的今天,为什么大小端谁都没有被另外一个所同化?我想这除了历史的惯性使然,还与它们各自的优缺点有关。

        大端模式优点:符号位在所表示的数据的内存的第一个字节中,便于快速判断数据的正负和大小

        小端模式优点1. 内存的低地址处存放低字节,所以在强制转换数据时不需要调整字节的内容(注解:比如把int的4字节强制转换成short的2字节时,就直接把int数据存储的前两个字节给short就行,因为其前两个字节刚好就是最低的两个字节,符合转换逻辑); 2. CPU做数值运算时从内存中依顺序依次从低位到高位取数据进行运算,直到最后刷新最高位的符号位,这样的运算方式会更高效

        其各自的优点就是对方的缺点,因此在多字节存储顺序上始终没有一个统一的标准

4 redis源码

      主要的是文件头的一段注释。“方法不会被直接调用,但是会被endianconv.h的宏定义使用,如果存储文件是小端,不需要定义其他操作。redis 尽量把所有的多字节类型按照小端编码(但是有些场景要向后兼容,仍然是大端),是因为大多数生产环境是小端;在内存中ziplist、intset、zipmap在内存中是endin-中性的(what is nuetral)场景,需要很多转换操作,因为在写磁盘持久化的时候直接系统调动write(2),而没有其他步骤。”
* This functions are never called directly, but always using the macros
 * defined into endianconv.h, this way we define everything is a non-operation
 * if the arch is already little endian.
 *
 * Redis tries to encode everything as little endian (but a few things that need
 * to be backward compatible are still in big endian) because most of the
 * production environments are little endian, and we have a lot of conversions
 * in a few places because ziplists, intsets, zipmaps, need to be endian-neutral
 * even in memory, since they are serialied on RDB files directly with a single
 * write(2) without other additional steps.

       只有在宿主机是大端的情况下,定义的宏才真正有效,小端情况无需操作。XXXXifbe方法是其他文件调用endianrev的方式。

/* variants of the function doing the actual convertion only if the target
 * host is big endian */
#if (BYTE_ORDER == LITTLE_ENDIAN)
#define memrev16ifbe(p)
#define memrev32ifbe(p)
#define memrev64ifbe(p)
#define intrev16ifbe(v) (v)
#define intrev32ifbe(v) (v)
#define intrev64ifbe(v) (v)
#else
#define memrev16ifbe(p) memrev16(p)
#define memrev32ifbe(p) memrev32(p)
#define memrev64ifbe(p) memrev64(p)
#define intrev16ifbe(v) intrev16(v)
#define intrev32ifbe(v) intrev32(v)
#define intrev64ifbe(v) intrev64(v)
#endif

         

       实现部分,记住两点即可(1)逆序(2)无符号数

/* Toggle the 16 bit unsigned integer pointed by *p from little endian to
 * big endian */
void memrev16(void *p) {
    unsigned char *x = p, t;

    t = x[0];
    x[0] = x[1];
    x[1] = t;
}

/* Toggle the 32 bit unsigned integer pointed by *p from little endian to
 * big endian */
void memrev32(void *p) {
    unsigned char *x = p, t;

    t = x[0];
    x[0] = x[3];
    x[3] = t;
    t = x[1];
    x[1] = x[2];
    x[2] = t;
}

/* Toggle the 64 bit unsigned integer pointed by *p from little endian to
 * big endian */
void memrev64(void *p) {
    unsigned char *x = p, t;

    t = x[0];
    x[0] = x[7];
    x[7] = t;
    t = x[1];
    x[1] = x[6];
    x[6] = t;
    t = x[2];
    x[2] = x[5];
    x[5] = t;
    t = x[3];
    x[3] = x[4];
    x[4] = t;
}


参考:http://www.metsky.com/archives/574.html

        http://www.cnblogs.com/wuyuegb2312/archive/2013/06/08/3126510.html

        https://jocent.me/2017/07/25/big-little-endian.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值