在 Intel CPU 和 Arm CPU 中,用户空间的指针地址默认都只使用低 48 位,高16 位总是 0。
写一小段代码验证下:
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
void o(long long ptr)
{
printf("%016p: ", (void *)ptr);
for (int i = 0; i < 8; ++i) {
printf("%02x.", (ptr << (i * 8)) >> (7 * 8) & 0xFF);
}
printf("\n");
}
int main(int argc, const char *argv[])
{
int a = 10;
void *ptr1 = (void *)&a;
void *ptr2 = malloc(10);
o((long long)(ptr1));
o((long long)(ptr2));
return 0;
}
输出如下,可以印证高 16 位总是为 0.
0x0000016bd2f42c: 00.00.00.01.6b.d2.f4.2c.
0x00600000268010: 00.00.60.00.00.26.80.10.
为什么高 16 位总是为 0?这是因为,就目前的应用程序需求来说,只使用低 48 位已经足够。位数越少,CPU 构建成本越低。
对于 ARM 处理器,可以看到下面的描述。也就是说,48 bit 不是定数,未来也可能变化。
All Armv8-A implementations support 48-bit virtual addresses. Support for 52-bit or 56-bit virtual addresses is optional and reported by ID_AA64MMFR2_EL1. At the time of writing, none of the Arm Cortex-A processors support 52-bit virtual addresses.
基于这样一个事实,在部分场景下我们可以对空闲的高 16 位加以利用,存储一些辅助 flag,这样可以让内存结构更加紧凑。一个典型应用场景是 Hash 表,我们可以这么定义 Slot:
struct HashSlotEntry {
uintptr_t ptr: 48;
bool flag1: 1;
bool flag2: 1;
bool flag3: 1;
bool flag4: 1;
bool flag5: 1;
bool flag6: 1;
bool flag7: 1;
bool flag8: 1;
char info: 8;
};
如此这样,我们就可以在 HashSlot 里存下更多信息了。