【问题标题】:How do I take the address of one past the end of an array if the last address is 0xFFFFFFFF?如果最后一个地址是 0xFFFFFFFF,如何获取数组末尾的地址?
【发布时间】:2023-03-13 22:40:02
【问题描述】:

如果将地址放在数组末尾之后是合法的,如果数组地址的最后一个元素是0xFFFFFFFF,我该怎么做?

这段代码是如何工作的:

for (vector<char>::iterator it = vector_.begin(), it != vector_.end(); ++it)
{
}

编辑:

在提出这个问题之前,我在这里读到这是合法的:May I take the address of the one-past-the-end element of an array?

【问题讨论】:

  • 是什么让您认为0xFFFFFFFF 是最大的可能元素?那么 64 位架构呢?
  • 访问过去的指针是合法的。创建它们是合法的,而不是取消引用它们。
  • @Chris 这就是我想知道的,如果我做出任何你可以澄清的无效假设。我们只说 32 位。
  • 例如,如果您有一个vector&lt;char&gt;,它不会通过将大小添加到基数来计算end(),因此end() 将是地址0,因为溢出?如果是这样,它应该可以正常工作,因为您的迭代器也会溢出到0 并正常停止。
  • @SethCarnegie: end() 返回一个迭代器,而不是一个指针,因此它不能返回一个空指针。没有直接等效的“空迭代器”。

标签: c++


【解决方案1】:

很多会受此影响的架构通过为操作系统保留偏移量0xFFFFFFFF(以及更多)来解决问题。

【讨论】:

    【解决方案2】:

    有时对于极端情况,您能做的最好的事情就是禁止它们。我在 NS32032 的一些位域提取指令中看到了此类问题,其中硬件将从字节地址开始加载 32 位并从该数据中提取。因此,即使映射内存的最后 3 个字节中的任何单个位字段也会失败。解决方案是永远不允许最后 4 个字节的内存可供分配。

    【讨论】:

      【解决方案3】:
      • 在数组末尾访问合法
      • 代码实际上并没有访问那个地址。
      • 而且您永远不会在真实系统上为您的对象获得这样的地址。

      【讨论】:

      • 我认为这个问题更多地与溢出整数有关,而不是非法访问内存。
      • 如果指针溢出到 0 是合法的,实现将不得不做一些疯狂的体操来确保 ptr_one_past_end &gt; ptr_start
      【解决方案4】:

      我认为有一个很好的论据表明符合 C 的实现不允许数组以(例如)0xFFFFFFFF 结尾。

      p 成为指向数组末尾一个元素的指针:如果buffer 被声明为char buffer[BUFFSIZE],那么p = buffer+BUFFSIZEp = &amp;buffer[BUFFSIZE]。 (后者含义相同,其有效性在 C99 标准文档中已明确。)

      然后我们期望指针比较的普通规则起作用,因为p 的初始化是一个普通的指针算术位。 (您不能比较标准 C 中的任意指针,但如果它们都基于单个数组、内存缓冲区或结构,则可以比较它们。)但是如果 buffer 以 0xFFFFFFFF 结束,那么 p 将是 0x00000000,并且p &lt; buffer!

      这将破坏许多现有代码,这些代码假定在相对于数组基数进行的有效指针算术中,直观的地址排序属性成立。

      【讨论】:

      • 符合标准的 C 实现可以使用不直接映射到内存地址的内部指针表示。例如,它可能决定内存地址N 由指针值N-1 表示。这样的实现通过牺牲访问字节 1 的能力来获得访问字节 0xFFFFFFFF 的能力。Win32s 以这种方式工作(除了偏差不是 1 而是大约 4MB。)
      【解决方案5】:

      超出数组边界的访问是未定义的行为。如果恶魔从你的鼻子里飞出,你不应该感到惊讶(或类似的东西)

      实际上可能发生的是地址溢出,这可能导致您读取地址为零,从而导致分段错误。

      如果您始终在数组范围内,并且您执行最后一个超出数组的++it,并将其与_vector.end() 进行比较,那么您实际上并没有访问任何内容,应该不会有问题。

      【讨论】:

      • 该标准保证获取指向数组末尾元素的指针是合法的(当然取消引用它是合法的)。
      • @MatteoItalia 因此“那么您并没有真正访问任何东西,应该没有问题。”但是感谢您强调这一点
      • 正确,我再次强调它只是因为第一句话似乎给出了错误的想法。
      【解决方案6】:

      如果这种情况对于特定的体系结构来说是个问题(可能是也可能不是),那么编译器和运行时可能会安排分配的数组永远不会在0xFFFFFFFF 结束。如果他们未能做到这一点,并且当数组确实在那里结束时出现问题,那么他们将不符合 C++ 标准。

      【讨论】:

      • 在“正常”架构中,我认为这甚至都不是问题,因为堆栈位于堆之上,vector 存储其内容。
      • @GregHewgill 谢谢,这就是我的想法,你回答了我的问题!
      【解决方案7】:

      区别在于取消引用该元素和获取其地址。在您的示例中,结束后的元素不会被取消引用,因此它是有效的。虽然这在 C++ 的早期并不是很清楚,但现在很清楚。此外,您传递给下标的值并不重要。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-07
        • 1970-01-01
        相关资源
        最近更新 更多