什么时候指针减法在C中未定义?

时间:2012-08-23 20:46:42

标签: c arrays pointers undefined-behavior pointer-arithmetic

char *buf = malloc(bufsize)
char *ptr = buf;
…
while(condition) {
    ptrdiff_t offset = ptr - buf;    // <========== THIS LINE

    // offset will never be negative because we only ever *increase* ptr
    if ((size_t)offset > bufsize) {
        // we need more room
        bufsize += 128;
        buf = realloc(buf, bufsize);
        ptr = buf + offset;  // buf might be in a completely new location
    }
    *ptr++ = …  // write this byte
}

有效未定义

我原以为它是有效的,但是我读到了一些关于它未定义的内容,所以我用Google搜索了它。这些链接似乎不可避免地声称它未定义:

然而,在这些SO问题中没有提到它:

这些都谈论不是两个指针在同一个“数组”中。这实际上是指堆栈上的普通旧C数组吗?

如果它是未定义的,对我来说似乎很奇怪...当我有权访问一个常量指针和一个移动指针时,为什么强迫我携带一个计数器变量?

3 个答案:

答案 0 :(得分:6)

指向malloc返回的内存块的指针计入同一个数组:

  

     

7.22.3内存管理功能

     

1 -   如果分配成功[...]可能被分配给[malloc]返回的指针   指向任何类型的对象的指针然后使用   在分配的空间中访问这样的对象或这些对象的数组(直到空间   明确解除分配。)

答案 1 :(得分:2)

ptrdiff_t offset = ptr - buf;    // <========== THIS LINE

这是完全定义的行为。

  

(C99,6.5.6p9)“当减去两个指针时,两个指针都指向同一个数组对象的元素[...]”

答案 2 :(得分:0)

它是定义的行为,只要你没有超过数组末尾的一个元素。 C99§6.5.6/ 8说明了添加指针和整数:

  

[...]如果两个指针   操作数和结果指向同一个数组对象的元素,或者指向最后一个数组对象的元素   数组对象的元素,评估不得产生溢出;否则,   行为未定。 [...]

关于减法的第9段:

  

9)当减去两个指针时,两个指针都指向同一个数组对象的元素,   或者超过数组对象的最后一个元素; [...]

来自§7.20.3/ 1:

  

如果分配则返回指针   适当地对齐成功,以便可以将其指定给指向任何类型对象的指针   然后用于在分配的空间中访问此类对象或此类对象的数组   (直到空间被明确释放)。

因此,一旦将ptr移动到指向最后一个数组元素之后的元素之外,执行指针减法就是未定义的行为。

我相信有些系统会因为这个代码而表现不佳,尽管我不能说出任何名称。理论上,malloc()可以返回指向可寻址存储器结束之前的指针,例如,如果你要求255个字节,它可以在32位系统上返回0xFFFFFF00,因此创建一个超出末尾的指针将导致溢出。指针表示中的整数溢出也可能触发某种陷阱(例如,如果指针存储在特殊寄存器中)。虽然我不知道任何具有这些属性的系统,但C标准当然允许它们存在。