从普通指针中减去 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 倍以上。
这是 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 倍以上。