哪些C编译器有指针减法下溢?

时间:2009-04-24 06:43:48

标签: c arrays pointers compiler-construction pointer-arithmetic

因此,当我从Michael Burr的评论中学习到this answer时,C标准不支持从数组中第一个元素之后的指针进行整数减法(我想这包括任何已分配的内存) )。

来自the combined C99 + TC1 + TC2(pdf)的第6.5.6节:

  

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

我喜欢指针算法,但这从来都不是我以前所担心的。我一直认为给定:

 int a[1];
 int * b = a - 3;
 int * c = b + 3;

c == a

因此,虽然我相信我以前做过那种事情而且没有被咬过,但这一定是由于我曾与之合作的各种编译器的善意 - 他们已经超越了标准需要使指针算术以我认为的方式工作。

所以我的问题是,这有多常见?是否有常用的编译器不能为我做那种善意?超出数组范围的正确指针算法是一个事实上的标准吗?

4 个答案:

答案 0 :(得分:7)

MSDOS FAR指针存在这样的问题,通常在实模式下“聪明地”使用段寄存器与偏移寄存器的重叠。其结果是16位段向左移4位,并加到16位偏移量,得到20位物理地址,可以解决1MB,这很多,因为每个人都知道没有人会像多达640KB的RAM。 ; - )

在保护模式下,段寄存器实际上是内存描述符表的索引。典型的DOS扩展运行时通常会安排事情,以便可以像处理实模式一样处理许多段,这使得从实模式移植代码变得容易。但它有一些缺陷。主要是分配之前的段不是分配的一部分,因此它的描述符甚至可能无效。

在处于受保护模式的80286上,只是加载一个段寄存器,其值会导致无效的描述符加载,这会导致异常,无论该描述符是否实际用于引用内存。

类似的问题可能发生在分配后的一个字节处。指针上的最后一个++可能已经转移到段寄存器,导致它加载一个新的描述符。在这种情况下,可以合理地预期内存分配器可以在分配范围的末尾之前安排一个安全描述符,但是期望它安排除此之外是不合理的。

答案 1 :(得分:4)

这不是标准的“实施定义”,标准是“未定义的”。这意味着你不能指望支持它的编译器,你不能说,“好吧,这个代码在编译器X上是安全的”。通过调用未定义的行为,您的程序是未定义的。

实际的答案不是“如何(何时,何时,在什么编译器上)我可以逃脱这个”;实际的答案是“不要这样做”。

答案 2 :(得分:1)

另一个原因是有可选的保守垃圾收集器(如boehm-weiser GC)假设指针总是在分配的范围内,如果不是,则允许它们随时释放内存。

有一个流行的商业品质和使用过的库确实打破了这个假设,惠普的Judy Trees Library使用指针算法来实现非常复杂的哈希结构。

答案 3 :(得分:0)

对于TI Explorer,

ZETA-C;指针实现为数组和索引或移位数组,IIRC,所以你的例子可能不起作用。从zcprim>pointer-subtract中的zcprim.lisp开始,找出行为是什么。根据标准不知道这是否正确,但我得到的印象是它。