指针减法计算地址间距的疑问:为何结果是类型个数而非字节数?
这是个非常常见的C语言指针算术误区,我来帮你理清楚背后的标准规定和实际原因:
先拆解你观察到的现象
你遇到的结果完全符合C语言的指针减法规则:
- 当
a和b是uint32_t类型时:假设两个变量的地址字节差是4(大概率是栈对齐导致的相邻分配),指针减法&b - &a会自动除以sizeof(uint32_t)(也就是4),所以结果是4/4=1。 - 当
a和b是uint8_t类型时:同样地址字节差为4,指针减法除以sizeof(uint8_t)(也就是1),结果就是4/1=4。
这里的核心结论是:指针减法的结果是两个指针之间的元素个数差值,而非原始字节数差值。
C语言标准的明确规定
以C11标准为例,6.5.6 加法运算符章节对指针减法的行为做了清晰定义:
当两个指针相减时,两者都必须指向同一个数组对象的元素,或者指向该数组对象的最后一个元素之后的位置;结果是两个数组元素的下标之差。结果的大小由实现定义,其类型是有符号整数类型
ptrdiff_t。如果结果无法用该类型的对象表示,则行为未定义。换句话说,如果表达式P和Q分别指向数组对象的第i个和第j个元素,那么表达式(P)-(Q)的值为i-j,前提是该值能放入ptrdiff_t类型的对象中。此外,如果表达式P指向数组对象的一个元素或最后一个元素之后的位置,表达式Q指向同一数组对象的最后一个元素,那么表达式((Q)+1)-(P)的值与((Q)-(P))+1以及-((P)-((Q)+1))相同;如果P指向数组对象的最后一个元素之后的位置,则该值为零,即使(Q)+1并不指向数组对象的元素。
补充说明
你可能会疑惑:我的例子里a和b不是数组元素,为什么规则还适用?
实际上,C标准允许将单个变量视为长度为1的数组的唯一元素,所以两个相邻的局部变量(在编译器分配的连续地址空间中)可以类比为数组的两个元素,指针减法的规则同样生效。另外,你例子中地址字节差固定为4,大概率是因为编译环境开启了栈对齐(比如32位系统默认4字节对齐),即使是uint8_t类型的变量,也会被分配4字节的栈空间。
如何得到你预期的字节数差值
如果你想计算两个地址之间的原始字节数,可以把指针强制转换为uint8_t*(或char*,因为char类型的大小固定为1字节)再做减法:
uint32_t a, b; uint32_t length = (uint8_t*)&b - (uint8_t*)&a; // 直接得到字节数差值
内容的提问来源于stack exchange,提问作者Jeff

