11
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
}

这是有效的还是未定义的?

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

但是,在这些 SO 问题中没有提到它:

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

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

4

3 回答 3

6

malloccount 返回的指向同一数组的内存块的指针:

7.22.3 内存管理功能

1 -malloc如果分配成功 [...] 返回的指针 [...] 可以分配给指向任何类型对象 [...] 的指针,然后用于访问空间中的此类对象或此类对象的数组分配(直到空间被显式释放)。

于 2012-08-23T20:50:47.210 回答
2
ptrdiff_t offset = ptr - buf;    // <========== THIS LINE

这是完全定义的行为。

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

于 2012-08-23T20:55:23.157 回答
1

这是定义的行为,只要您不超过数组末尾的一个元素。C99 §6.5.6/8 说这个关于添加一个指针和一个整数:

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

第 9 段,关于减法:

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

从 §7.20.3/1 开始:

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

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

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

于 2012-08-23T21:12:50.437 回答