从普通指针中减去 NULL 指针生成算术右移

Subtracting NULL pointer from a normal pointer generates arithmetic right shift

这是 C 代码。

int main() {
  int i = 10, *p = &i;
  printf("%ld", p - (int *) NULL);
}

对于指针算术部分,'gcc'和'clang'都在它们的汇编输出中生成一条'sar rax, 2'指令。有人可以解释一下这种情况下的指针运算与算术右移有何关系。

右移 2 是除以 4 的快速方法。4 是您的 int 大小。

两个指针到int的距离是int指针对应的两个char指针的距离除以int大小(记住,添加一个整数到指针,整数按指针目标大小缩放,所以当你做差异时,你需要撤消这个缩放)。

从技术上讲,您不应该减去两个不相关的指针(或使用 "%ld" 而不是正确的 "%zd" 打印差异),因为这是未定义的行为——标准只允许您比较指针指向同一个对象或刚刚过去。尽管如此,一个通用的 int*-diffing 函数本身并没有这种未定义的行为:

#include <stddef.h>

ptrdiff_t diff_int_ptr(int *A, int *B)
{
    return A-B;
}

仍将转换为

ptrdiff_t diff_int_ptr(int *A, int *B)
{
    return ((char*)A - (char*)B) >> lg2(sizeof(int));
    //strength reduced from: return ((char*)A - (char*)B) / sizeof(int)
    //which is possible iff sizeof(int) is a power of 2
}

在优化编译器 (godbold link for x86-64 gcc and clang) 上,因为移位通常比现代机器上的除法快 10 倍以上。