普通指针减去NULL指针为何会生成算术右移(sar)指令?
为什么指针运算
p - (int*)NULL会生成sar rax, 2指令? 让我们一步步拆解这个问题,从C标准规则到汇编指令的本质:
1. 先看懂C代码里的指针运算逻辑
先看你的代码:
int main() { int i = 10, *p = &i; printf("%ld", p - (int *) NULL); }
这里的核心运算p - (int*)NULL,遵循C语言的指针减法规则:
- 只有同类型指针相减时,结果才是它们之间相隔的元素个数,而不是字节数。
(int*)NULL本质是把地址0转换成int*类型的指针,所以这个运算的实际意义是:计算指针p的地址值相对于地址0的int元素数量。
举个例子:如果&i的地址是0x1004,那0x1004 / 4 = 0x401(因为int占4字节),这个结果就是p - (int*)NULL的值。
2. 为什么用sar rax, 2来实现这个计算?
在x86_64平台上,int的大小通常是4字节(32位),所以元素个数 = 指针地址值 ÷ 4。而sar rax, 2是算术右移2位的汇编指令,它和除以4的关系是:
- 对于正数来说,算术右移n位等价于除以
2^n。右移2位就是除以2^2=4,和我们需要的计算完全匹配。 - 为什么用算术右移(
sar)而不是逻辑右移(shr)?因为C语言中指针减法的结果类型是ptrdiff_t,这是一个有符号整数类型。编译器会把指针地址当作有符号数处理,算术右移会保留符号位,能正确处理负数地址(虽然用户空间几乎不会遇到,但内核地址可能存在负数情况),而逻辑右移会补0,对负数地址处理不正确。
所以编译器把指针地址加载到rax寄存器后,用sar rax,2一步完成“除以4得到元素个数”的计算,这就是两者的关联。
内容的提问来源于stack exchange,提问作者phrack101
相关产品推荐
相关产品推荐

