是否存储无效指针自动未定义的行为?

时间:2010-10-01 11:51:13

标签: c++ c pointers

显然,取消引用无效指针会导致未定义的行为。但是,只是在指针变量中存储无效的内存地址呢?

请考虑以下代码:

const char* str = "abcdef";
const char* begin = str;
if (begin - 1 < str) { /* ... do something ... */ }

表达式begin - 1计算为无效的内存地址。请注意,我们实际上并没有取消引用此地址 - 我们只是在指针算法中使用它来测试它是否有效。尽管如此,我们仍然需要将无效的内存地址加载到寄存器中。

那么,这是不确定的行为吗?我从来没有想过,因为很多指针算法似乎依赖于这种事情,而指针实际上只是一个整数。但最近我听说即使将无效指针加载到寄存器中的行为也是未定义的行为,因为某些架构会自动抛出总线错误或者其他内容。任何人都可以向我指出C或C ++标准的相关部分,无论哪种方式解决这个问题?

7 个答案:

答案 0 :(得分:14)

我在这里有C草案标准,并且由于遗漏而未定义。

ptr + I定义了6.5.6 / 8的情况
  • 如果指针操作数指向数组对象的元素,并且数组足够大,则结果指向与原始元素偏移的元素,使得结果元素和原始数组元素的下标的差异等于整数表达式。
  • 此外,如果表达式P指向数组对象的最后一个元素,则表达式(P)+1指向数组对象的最后一个元素之后的一个,并且如果表达式Q指向一个超过一个数组对象的最后一个元素的一个元素。数组对象,表达式(Q)-1指向数组对象的最后一个元素。

您的案件不适合任何此类案件。您的数组也不足以让-1调整指针以指向不同的数组元素,也不会将任何结果或原始指针指向一个接收端。

答案 1 :(得分:10)

由于其他原因,您的代码是未定义的行为:

表达式begin - 1 会产生无效指针。这是未定义的行为。您不能在超出您正在处理的数组范围内执行指针算术。因此减法本身是无效的,而不是存储结果指针的行为。

答案 2 :(得分:6)

某些体系结构具有用于保存指针的专用寄存器。将未映射的地址的值放入这样的寄存器中是允许崩溃的。允许整数溢出/下溢崩溃。由于C的目标是在各种平台上工作,因此指针提供了一种安全编程不安全电路的机制。

如果您知道自己不会在具有如此挑剔特性的异国硬件上运行,则无需担心语言未定义的内容。它由平台很好地定义。

当然,这个例子的风格很差,并没有充分的理由去做。

答案 3 :(得分:3)

使用无效指针会产生未定义的行为。我在这里没有C标准,但在基本原理中看到“无效指针”:http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf

答案 4 :(得分:1)

  

$ 5.7 / 6 - “除非两个指针都指向   到同一个数组对象的元素,   或者一个过去的最后一个元素   数组对象,行为是   undefined.75)“

总结,即使您没有取消引用指针,也未定义

答案 5 :(得分:1)

多年前已经给出了正确答案,但我觉得有趣的是C99 rationale [秒。 6.5.6,最后3段]解释了为什么标准支持将1添加到指向数组的最后一个元素(p+1)的指针:

  

对广泛实践的一个重要认可是要求指针总是可以递增到刚好超过数组的末尾,而不用担心溢出或环绕

以及p-1未被认可的原因:

  

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

因此,如果指针p指向一个可寻址内存范围开头的对象(由此注释支持),那么p-1将产生下溢。

请注意,整数溢出是未定义行为的标准示例[秒。 3.4.3],因为它取决于翻译环境和操作环境。我相信很容易看出这种对环境的依赖性扩展到指针下溢。

这就是为什么标准明确地使其成为未定义的行为[在6.5.6 / 8],正如其他答案所述。引用那句话:

  

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

另见[秒。 C99基本原理的6.3.2.3,最后4段],它更详细地描述了如何生成无效指针,以及可能产生的影响。

答案 6 :(得分:0)

是的,这是未定义的行为。见the accepted answerthis closely related question。为变量分配无效指针,比较无效指针,转换无效指针会触发未定义的行为。