现代计算机存在两种数据的保存方式。
大端,即数据的低字节存储在高地址处。
小端,即数据的低字节存储在低地址处。
目前大多计算机的数据保存方式都是小端。
举个例子:
我们考虑数 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] 个字节。
对于每个字节,有一位表示该位是否是数据的最后一位。
考虑 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,235−1]
但是对于我们来说,最高的两位都是 0 0 0 ,对于结果没有意义,所以可以直接舍去。
得到的表示为:
1001011 | 0101101 | 0000111
这样原本一个需要 32 32 32 位 4 4 4 个字节的数,只需要 3 3 3 个字节即可表示。
我们考虑在主机序中的表示。
在人的理解中:从高字节到低字节
第一个字节是 1001011
第二个字节是 0101101
第三个字节是 0000111
因为我们是小端,就是数据的低字节在低地址处,
我们考虑从左到右地址是递增的。
那么存储的就是
1'0000111 | 1'0101101 | 0'1001011
即
135 | 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