C++数组能否在内存边界结束



C++标准(以及C)允许创建(而不是取消引用)指向数组末尾之后的一个元素的指针。这是否意味着数组永远不会被分配到其最后一个元素终止于内存边界的位置?我知道,在实践中,一些/所有实施可能会遵循这一惯例,但以下哪一项是正确的:

  1. 它实际上是false,数组可能在内存边界结束,或者
  2. C++标准要求在边界OR之前结束至少一个元素的内存
  3. 既不是1,也不是2,但它仍然像实际编译器中的那样,因为它使实现更容易

C的情况有什么不同吗?

更新:似乎1是正确的答案。请参阅下面James Kanze的回答,也请参阅efence(http://linux.die.net/man/3/efence-感谢Michael Chastain的指针)

实现必须允许指针指向存在它是如何做到这一点的。在许多机器上可以安全地将任何值放入指针,而没有风险(除非您取消引用它);在这样的系统中指针可能指向未映射的内存—我已经实际上在Windows下遇到了一个案例。

在其他机器上,只需加载指向未映射内存的指针进入寄存器将陷入陷阱,导致程序崩溃。在…上这样的机器,实现必须确保通过拒绝使用的最后一个字节或单词分配的内存,或者通过确保指针的所有使用除了取消引用之外,它还避免了任何可能导致硬件将其视为无效指针。(大多数系统具有单独的地址和数据寄存器,并且仅如果指针加载到地址寄存器中,则陷阱。如果数据寄存器足够大,编译器可以安全地加载将指针插入数据寄存器以进行例如比较。这是通常是必要的,因为地址寄存器并不总是支持比较。)

关于你的最后一个问题:C和C++在这方面完全相同;C++只是接管了C.的规则

指向void的指针或指向对象类型的指针的类型称为对象指针类型。[…]对象指针类型的有效值表示内存中字节的地址(1.7)或空指针(4.10)

连同§5.7/5[加法运算符]中的文本:

[…]此外如果表达式p指向数组对象的最后一个元素,表达式(P)+1指向数组对象的最后一个元素,如果表达式Q指向在数组对象的最后一个元素之后,表达式(Q)-1指向数组的最后一元素对象

如果要求超过结束指针的数组必须有效,那么似乎无法分配以内存中最后一个字节结尾的数组。如果允许结束指针后的那个无效,我不知道答案。

§3.7.4.2/4节【解除定位功能】规定:

使用无效指针值的效果(包括将其传递给解除分配函数)是未定义的。

因此,如果必须支持对已分配数组的超过结束指针的数组进行比较,则超过终止指针的数组必须有效。

根据我得到的评论,我假设一个实现可以分配一个数组,而不必关心数组的结束指针是否可用。然而,我想找出标准中的相关段落。

你说对了一半。假设一个假设的实现使用线性寻址的内存和指针,这些指针表示为16位无符号整数。还假设空指针表示为零。最后,假设您使用char *p = malloc(16);请求16字节的内存。然后可以保证您将得到一个数值小于65520的指针。值65520本身是无效的,因为正如您正确指出的,假设分配成功,p + 16是一个有效的指针,不能是空指针。

然而,现在假设一个假设的实现使用线性寻址的内存和指针,这些内存和指针表示为32位无符号整数,但只有16位的地址空间。再次假设空指针表示为零。最后,再次假设您使用char *p = malloc(16);请求16字节的内存。然后只能保证你会得到一个数值小于或等于65520的指针。值65520本身是有效的,只要实现确保将16加在上面得到值65536,减去16就可以回到65520。即使在地址65536处根本不存在内存(物理或虚拟),这也是有效的。

标准明确说明了当指针增加到最后一个元素时会发生什么。它为您提供了一个值,该值只能用作比较,以检查您是否在数组末尾或之前。指针很可能指向为其他对象有效分配的内存,但这是完全未定义的(实现定义的?)行为,因此使用该指针肯定是未定义的行为。

我得到的是,超过结束指针的指针就是:它是当你将指针增加到最后一个元素时得到的指针,以一种非常便宜的方式标记数组的结束。但请注意,比较不相关对象的指针是完全没有意义的(如果我没有错的话,甚至是未定义的行为)。因此,不同对象之间的指针"值"可能存在重叠这一事实是不成问题的,因为利用这一点,你就进入了未定义行为的领域。。

这取决于实现。至少在visual C++中,在不使用任何数组绑定检查的情况下,您可以创建一个指针,该指针超过数组末尾任意数量的元素。如果您取消引用它,只要您正在访问的内存地址在程序的已分配堆/堆栈中,它仍然可以工作。您将读取/修改该内存位置中的任何值。如果地址在分配的内存空间之外,则会出现错误。

调试器有检测这些错误的检查,因为这种编码会产生很难跟踪的错误。

最新更新