【问题标题】:Are comparisons on out-of-range pointers well-defined?超出范围指针的比较是否定义明确?
【发布时间】:2012-12-21 21:01:36
【问题描述】:

给定以下代码:

char buffer[1024];
char * const begin = buffer;
char * const end = buffer + 1024;
char *p = begin + 2000;
if (p < begin || p > end)
    std::cout << "pointer is out of range\n";

执行的比较(p &lt; beginp &gt; end)是否定义明确?或者这段代码是否有未定义的行为,因为指针已经超过了数组的末尾?

如果比较定义明确,那么定义是什么?

(额外的功劳:begin + 2000 本身的评估是未定义的行为吗?)

【问题讨论】:

  • 我认为答案只是指针越过末尾和NULL,但我不能引用章节。
  • 在具有分段内存的系统上,地址begin + 2000 可能不存在。
  • 本题有两个不同的概念:指针的比较和指针值的创建。在询问指针比较之前,我们必须确定begin + 2000 是否定义明确。我不认为它是。但是,一旦我们有了两个有效的指针,您就可以询问如何比较它们。正如 Mat 的回答所指出的,一般没有具体说明。但是,您可以使用std::less&lt;&gt;(等人)来获得指针值的总排序,即使未指定直接使用&lt;

标签: c++ language-lawyer


【解决方案1】:

我将假设 C++11 标准。根据第 5.7 节(加法操作数)第 5 段,*p = begin + 2000 的行为首先是未定义的,甚至在您进行比较之前:

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

【讨论】:

    【解决方案2】:

    begin+2000 的求值未定义,它超出了数组的末尾 - 您可以在末尾上升一个,但不能再进一步。

    来自 C++11 §5.7/5 加法运算符

    当一个整数类型的表达式被添加到指针或从指针中减去时,结果具有指针操作数的类型。如果指针操作数指向数组对象的元素,并且数组足够大,则结果指向与原始元素偏移的元素,使得结果和原始数组元素的下标之差等于积分表达式。 [...] 如果指针操作数和结果都指向同一个数组对象的元素,或者超过数组对象的最后一个元素,则评估不应产生溢出; 否则,行为是 未定义

    对于要指定的指针比较,假设您有有效的指针以开始,它们本质上需要是指向同一数组(或结束后一个)的指针,或者指向同一访问控制的非静态数据成员的指针同一个对象(除非它是一个联合......)。

    详细信息在 §5.9/2 中关系运算符

    可以比较指向相同类型的对象或函数的指针(指针转换后),结果定义如下:

    • 如果相同类型的两个指针 p 和 q 指向同一个对象或函数,或者都指向过去 相同数组的结尾,或者都为空,则 p=q 都产生 true 并且 pq 两者都产生错误。
    • 如果两个相同类型的指针 p 和 q 指向不同的对象,它们不是相同的成员 同一数组或不同函数的对象或元素,或者如果其中只有一个为空,则结果 pq、p=q 的数量未指定。
    • 如果两个指针指向同一个对象的非静态数据成员,或者指向子对象或数组元素 在此类成员中,递归地,指向后面声明的成员的指针比较大,前提是 两个成员具有相同的访问控制(第 11 条),并且他们的类不是联合。
    • 如果两个指针指向具有不同访问控制的同一对象的非静态数据成员 (第 11 条)结果未指定。 — 如果两个指针指向同一个联合对象的非静态数据成员,它们比较相等(在 如有必要,转换为 void*)。如果两个指针指向同一个数组的元素或一个以外的元素 在数组的末尾,指向具有较高下标的对象的指针比较高。
    • 未指定其他指针比较。

    【讨论】:

    • 至于为什么会这样,可以想象buffer 可以分配在一个只有 2048 字节大的内存中的地址 500。所以begin 将是 500,p 将是 2500。但是由于内存只有 2048 大(因此指针只有 11 位长),所以您实际得到的 p 是地址 452 -- buffer 的前面。 (是的,这个例子是人为的,但是有各种 实际 分段内存方案,它们并没有不同。)
    • 请注意,您可以使用 std::less&lt;&gt; (et. al.) 来获得不相关指针的总排序(当然,它们必须仍然具有有效值,这完全不同) .
    • 当然,无关指针的“排序”在很大程度上是没有意义的。
    • @Mat 它没有指向任何东西。没有什么“那里”;这是无效的。我把“那里”放在引号中,因为指针值首先不能合法地计算。那里甚至没有位置。查看其他答案。
    【解决方案3】:

    您的程序的行为未定义,但不是因为比较。

    表达式 begin + 2000 的计算具有未定义的行为,因为结果将指向超过 1024 个元素数组末尾的多个元素。

    引用 C++11(实际上是 N3485 草案),5.7p4 [expr.add]:

    当具有整数类型的表达式被添加或减去时 从一个指针,结果具有指针操作数的类型。 [...] 如果指针操作数和结果都指向 相同的数组对象,或数组对象的最后一个元素, 评估不应产生溢出;否则,行为 未定义。

    简而言之,仅计算越界指针具有未定义的行为;之后你对该指针执行什么操作并不重要。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-30
      • 2011-01-17
      • 1970-01-01
      • 2015-10-03
      • 2016-05-25
      • 1970-01-01
      • 2018-05-02
      • 1970-01-01
      相关资源
      最近更新 更多