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

显然,解引用无效指针会导致未定义的行为。 但是如何简单地存储一个无效的内存地址在指针variables?

考虑下面的代码:

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

expression式begin - 1评估为无效的内存地址。 请注意,我们实际上并没有取消引用这个地址 – 我们只是在指针运算中使用它来testing它是否有效。 尽pipe如此,我们仍然需要将一个无效的内存地址加载到一个寄存器中。

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

我有C草案标准在这里,它使它不确定的遗漏。 它定义了ptr + I在6.5.6 / 8的情况

  • 如果指针操作数指向数组对象的一个​​元素,并且数组足够大,则结果指向与原始元素偏移的元素,使得结果数组元素和原始数组元素的下标之差等于整数expression式。
  • 此外,如果expression式P指向数组对象的最后一个元素,则expression式(P)+1将指向数组对象的最后一个元素,如果expression式Q指向数组对象的最后一个元素之后,expression式(Q)-1指向数组对象的最后一个元素。

你的情况不适合任何这些。 你的数组也不够大,无法将指针调整为指向不同的数组元素,结果或原始指针也不会指向前一个末尾。

您的代码是未定义的行为,原因不同:

expression式begin - 1不会产生无效的指针。 这是未定义的行为。 你不能在指定数组的范围之外执行指针运算。 所以减法本身是无效的,而不是存储结果指针的行为。

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

如果你知道你不会在具有这种挑剔的特性的异国情调的硬件上运行,你不需要担心什么是未定义的语言。 它是由平台明确的。

当然,这个例子是不好的风格,没有一个好的理由去做。

任何使用无效指针都会导致不确定的行为。 我在这里没有C标准,但是请参阅理论依据中的“无效指针”: http : //www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf

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

总结, 即使不取消引用指针 ,它也是未定义

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

普遍实践的一个重要认可是要求指针总是可以增加到超过一个数组的末尾,而不用担心溢出或环绕

为什么p-1不被认可:

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

因此,如果指针p指向一个由该注释支持的可寻址内存范围的开始处的对象,则p-1将生成一个下溢。

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

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

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

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

是的,这是不确定的行为。 看到这个密切相关的问题 的接受答案 。 为一个variables指定一个无效的指针,比较一个无效的指针,投射一个无效的指针触发未定义的行为。