谈谈大端小端和变长编码

大端小端

现代计算机存在两种数据的保存方式。

大端,即数据的低字节存储在高地址处。
小端,即数据的低字节存储在低地址处。

目前大多计算机的数据保存方式都是小端。

举个例子:

我们考虑数 1234567

其无符号32位表示,在人的理解看来如下,其中每一部分是一个字节。

00000000 | 00010010 | 11010110 | 10000111

而实际

#include 
using namespace std;
void print_bit(uint32_t x) {
    for (int i = 0; i < 32; i++) {
        cout << ((x >> i) & 1); // 每个 bit 表示
        if (i == 31) {
            cout << endl;
        } else if ((i + 1) % 8 == 0) {
            cout << " | ";
        }
    }
}

int main() 
{
	uint32_t v = 1234567;
	print_bit(v);
	return 0;
}

上述程序输出结果为:

11100001 | 01101011 | 01001000 | 00000000

可以发现输出的结果和人的理解完全相反。

这就是小端。

我们该如何考理解这个数据的低字节存储在低地址处呢?

在代码中,我们依次考虑 uint32_t 32 32 32 位的每一位,是从 0 0 0 31 31 31 的。这里表示地址是递增的,人的理解中的最低位是 10000111 ,对应到计算机中的是 11100001 ,即计算机的低地址存储了数据的低字节。

对于大端呢。

这里需要指出的是,大端通常用于网络字节序,小端通常用于主机字节序。即在网络传输过程中使用的数据用大端存储,在本地计算机使用的数据用小端存储。

代码如下:

#include 
#include 
void print_bit(uint32_t x) {
    for (int i = 0; i < 32; i++) {
        cout << ((x >> i) & 1); // 每个 bit 表示
        if (i == 31) {
            cout << endl;
        } else if ((i + 1) % 8 == 0) {
            cout << " | ";
        }
    }
}

int main() 
{
	uint32_t host_value = 1234567;
	uint32_t network_value = htonl(host_value);
	print_bit(network_value);
	return 0;
}

上述程序输出结果为:

00000000 | 01001000 | 01101011 | 11100001

可以发现的是,每个字节的顺序是和人的理解一致的。

但是每个字节的内部和人的理解正好相反,但是这部分你不需要去理解,你只知道按照字节为地址去解释是对的即可。

这里因为计算机是以字节为基本地址单位的。

varint 编码

对于一个 varint 编码,是为了节省一个数字占用的字节大小。
一个 uint32_t 的数,需要 [ 1 , 5 ] [1, 5] [1,5] 个字节。

对于每个字节,有一位表示该位是否是数据的最后一位。

  • 1 1 1 表示不是最后一位
  • 0 0 0 表示为最后一位

考虑 1234567
人的理解为:

00000000 | 00010010 | 11010110 | 10000111

其中每 7 7 7 位表示一个 varint 的一个字节
拆分为:

0000000 | 0000000 | 1001011 | 0101101 | 0000111

可以发现这里至多可以存储 35 35 35 比特的无符号整数。
即范围为: [ 0 , 2 35 − 1 ] [0, 2^{35}-1] [0,2351]

但是对于我们来说,最高的两位都是 0 0 0 ,对于结果没有意义,所以可以直接舍去。

得到的表示为:

1001011 | 0101101 | 0000111

这样原本一个需要 32 32 32 4 4 4 个字节的数,只需要 3 3 3 个字节即可表示。

我们考虑在主机序中的表示。
在人的理解中:从高字节到低字节

第一个字节是 1001011
第二个字节是 0101101
第三个字节是 0000111

因为我们是小端,就是数据的低字节在低地址处,
我们考虑从左到右地址是递增的。

那么存储的就是
1'0000111 | 1'0101101 | 0'1001011135 | 173 | 75

尝试编码考虑一下:

#include 
using namespace std;

char* EncodeVarint32(char* dst, uint32_t v) {
    static const int B = 128;
    uint8_t* ptr = reinterpret_cast<uint8_t*>(dst);
    while (v >= B) {
        *(ptr++) = v | B;
        v >>= 7;
    }
    *(ptr++) = static_cast<uint8_t>(v);
    return reinterpret_cast<char*>(ptr);
}

int main()
{
	uint32_t vv = 1234567;
    char* dst = new char[5];
    char* ptr = EncodeVarint32(dst, vv);
    int len = int(ptr - dst);

    for (int i = 0; i < len; ++i) {
        cout << int(*(unsigned char*)(dst + i));
        if (i + 1 == len) cout << endl;
        else cout << " | ";
    }
	return 0;
} 

输出为:

135 | 173 | 75

你可能感兴趣的:(#,CPP,c++,算法,开发语言)