C中指针减法期间的除法

division during pointer subtraction in C

考虑以下代码片段:

int *p;
/* Lets say p points to address 100 
   and sizeof(int) is 4 bytes. */
int *q = p+1;
unsigned long r = q-p;
/* r results in 1, hence for r = q-p 
   something is happening similar to r=(104-100)/4 */

当两个相同类型的指针相减时,在运行时期间是否会真正除以 sizeof(datatype),或者是否存在指针相减的其他机制。

C standard 声明了以下关于指针减法的内容(第 6.5.6p9 节):

When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object; the result is the difference of the subscripts of the two array elements. The size of the result is implementation-defined, and its type (a signed integer type) is ptrdiff_t defined in the header. If the result is not representable in an object of that type, the behavior is undefined. In other words, if the expressions P and Q point to, respectively, the i -th and j -th elements of an array object, the expression (P)-(Q) has the value i−j provided the value fits in an object of type ptrdiff_t . Moreover, if the expression P points either to an element of an array object or one past the last element of an array object, and the expression Q points to the last element of the same array object, the expression ((Q)+1)-(P) has the same value as ((Q)-(P))+1 and as -((P)-((Q)+1)) , and has the value zero if the expression P points one past the last element of the array object, even though the expression (Q)+1 does not point to an element of the array object. 106)

脚注 106 指出:

Another way to approach pointer arithmetic is first to convert the pointer(s) to character pointer(s): In this scheme the integer expression added to or subtracted from the converted pointer is first multiplied by the size of the object originally pointed to, and the resulting pointer is converted back to the original type. For pointer subtraction, the result of the difference between the character pointers is similarly divided by the size of the object originally pointed to. When viewed in this way, an implementation need only provide one extra byte (which may overlap another object in the program) just after the end of the object in order to satisfy the "one past the last element" requirements.

所以脚注指出指针减法可以通过减去原始指针值并除以指向对象的大小来实现。然而,它不必以这种方式实现。

另请注意,标准要求在指向同一数组对象的元素(或末尾后的一个元素)的指针之间执行指针减法。如果他们不这样做,那么行为是未定义的。在实践中,如果你在一个具有平面内存模型的系统上工作,你可能仍然会得到 "expected" 值,但你不能依赖它。

有关指针减法如何工作的解释,请参阅@dbush 回答。

相反,如果您正在编写一些低级的东西,比如内核、驱动程序、调试器或类似的东西,并且您需要实际减去地址,请将指针转换为 char *:

(char *)q - (char *)p

结果将是 ptrdiff_t 类型,一个实现定义的有符号整数。

当然,这不是 defined/portable C,但适用于大多数 architectures/environments。