限制指针算术或比较的基本原理是什么?

在C / C ++中,仅当结果指针位于原始指向的完整对象内时,才定义指针的加法或减法 。 此外,只有当两个指向对象是唯一完整对象的子对象时,才能执行两个指针的比较 。

这种限制的原因是什么?

我认为分段内存模型(参见此处 §1.2.1)可能是其中一个原因,但由于编译器实际上可以定义所有指针的总顺序,如此答案所示 ,我对此表示怀疑。

存在程序和数据空间分离的架构,并且根本不可能减去两个任意指针。 指向函数或const静态数据的指针将与正常变量位于完全不同的地址空间中。

即使您在不同地址空间之间任意提供排名, diff_t类型也可能需要更大的大小。 并且比较或减去两个指针的过程将非常复杂。 在一种专为速度而设计的语言中,这是一个坏主意。

你只能certificate限制可以被删除 – 但是错过它会带来成本(在内存和代码方面) – 这与C的目标相反。

具体来说,差异需要有一个类型,即ptrdiff_t,并且可以假设它类似于size_t。

在分段存储器模型中,您(通常)间接地限制对象的大小 – 假设答案在: 16位的“size_t”,“uintptr_t”,“intptr_t”和“ptrdiff_t”类型的实际大小是多少使用分段寻址模式的系统? 是正确的。

因此,至少对于消除该限制的差异,不仅会添加额外的指令以确保总顺序 – 对于不重要的角落情况(如在其他答案中),还要花费双倍的内存量来进行差异等。

C被设计为更简约,并且不强迫编译器在这种情况下花费内存和代码。 (在那些日子里,记忆限制更重要。)

显然还有其他好处 – 比如混合来自不同arrays的指针时检测错误的可能性。 类似地,在C ++中没有定义两个不同容器的混合迭代器(有一些小的例外) – 并且一些调试实现检测到这样的错误。

原因是保持生成合理代码的可能性。 这适用于具有平坦内存模型的系统以及具有更复杂内存模型的系统。 如果你禁止(不是非常有用的)极端情况,例如添加或减去数组,并要求对象之间的指针的总顺序,你可以跳过生成的代码中的大量开销。

标准施加的限制允许编译器对指针算法进行假设,并使用它来提高代码的质量。 它涵盖了在编译器中静态计算事物而不是在运行时,并选择使用哪些指令和寻址模式。 例如,考虑一个带有两个指针p1p2 。 如果编译器可以推导出它们指向不同的数据对象,则可以安全地假设基于后续p1任何操作都不会影响p2指向的对象。 这允许编译器基于p1重新排序加载和存储,而不考虑基于p2加载和存储, p2

基本原理是某些体系结构具有分段内存,而指向不同对象的指针可能指向不同的内存段。 这两个指针之间的差异不一定是有意义的。

这可以追溯到预标准C. C基本原理没有明确提到这一点,但它暗示了这是原因,如果我们看一下它解释为什么使用负数组索引是未定义行为的理由(C99理由5.10 6.5.6,强调我的):

另一方面,在p-1的情况下,必须在p遍历的对象数组之前分配整个对象,因此从数组底部运行的递减循环可能会失败。 例如,这种限制允许分段体系结构将对象放置在一系列可寻址存储器的开头。

由于C标准打算涵盖大多数处理器体系结构,它还应该涵盖这一个:想象一个架构(我知道一个,但不会命名),其中指针不仅仅是普通数字,而是像结构或“描述符” ”。 这样的结构包含有关它所指向的对象的信息(其虚拟地址和大小)以及其中的偏移量。 添加或减去指针会产生一个只调整了偏移字段的新结构; 产生偏移量大于对象大小的结构是硬件禁止的。 还有其他限制(例如如何生成初始描述符或修改它的其他方法),但它们与主题无关。

在Stanadrd将操作分类为调用未定义行为的大多数情况下,它已经这样做了,因为:

  1. 可能存在定义行为昂贵的平台。 如果代码尝试执行超出对象边界的指针算法,则分段体系结构可能表现得很奇怪,并且一些编译器可能通过测试qp的符号来评估p > q

  2. 有一些类型的编程,定义行为将是无用的。 许多类型的代码可以很好地获得,而不依赖于标准给出的指针加法,减法或关系比较的forms。

  3. 为各种目的编写编译器的人应该能够识别用于此类目的的质量编译器应该可预测的情况,并在适当时处理这些情况,无论标准是否迫使他们这样做。

#1和#2都很低,#3被认为是“gimme”。 尽管编译器编写者通过寻找破坏其行为是由用于低级编程的质量实现定义的代码的方法来展示他们的聪明才智已成为时尚,但我不认为标准的作者期望编译器编写者能够感知到巨大的行为可预测的行为之间的差异,与几乎所有质量实现预期行为相同的行为之间的差异,但在那里可能会让一些神秘的实现做其他事情是有用的。

我想通过颠倒这个问题来回答这个问题。 为什么指针只允许添加或减去一个整数,post和pre递增和递减以及指向同一个数组的指针的比较(或减法),而不是问为什么指针加法和大多数算术运算都不被允许? 它与算术运算的逻辑结果有关。 将整数n加到/减去指针p给出了当前指向元素在正向或反向的第n个元素的地址。 类似地,减去指向同一数组的p1和p2给出了两个指针之间的元素计数。 指针算术运算定义的事实(或设计)与它所指向的变量类型一致是真正的天才。 除了允许的操作之外的任何操作都不符合编程或哲学逻辑推理,因此是不允许的。

(请不要upvote)这是答案的摘要,以及我选择Mark Ransom答案的原因,以及发表这篇文章:答案是其post+以下评论。

问题是这种限制的原因什么(指针算术和比较必须落在一个独特的完整对象上)?

Ransom的评论+答案摘要:由于没有地址空间的概念,限制指针算法落入地址空间的唯一可能性是将它约束在一个对象上。

Krazy Glew评论也提供了一个社会学的答案。