当我们引用非静态数据成员时,分段错误是否是实际未定义的行为

9 c++ pointers undefined-behavior

我已经阅读了以下规则,并且我一直在尝试编写一个反映一个例子的例子.规则来自3.8/5 N3797:

在对象的生命周期开始之前但是在对象将占用的存储之后,或者在对象的生命周期结束之后以及在重用或释放对象占用的存储之前,任何指向存储的指针之前可以使用对象将位于或位于的位置,但仅限于有限的方式.对于正在建造或销毁的物体,见12.7.否则,这样的指针指的是已分配的存储(3.7.4.2),并且使用指针就像指针的类型一样void*是明确定义的.允许通过这样的指针的间接,但是得到的左值可以仅以有限的方式使用,如下所述.如果出现以下情况,该程

[...]

- 指针用于访问非静态数据成员或调用对象的非静态成员函数,或

[...]

我写的例子:

#include <iostream>
#include <typeinfo>

using std::cout;
using std::endl;

struct A
{
    int b = 5;
    static const int a = 5;
};

int main()
{
    A *p = (A*)0xa31a3442;
    cout << p -> a; //1, Well-fromed, there is no compile-time error
    cout << p -> b; //2, Segmentation fault is producing
}
Run Code Online (Sandbox Code Playgroud)

这是真的,在的情况下//1很好地形成的,不会造成任何UB的,但//2产生的段错误,这是UB

Bas*_*tch 24

未定义的行为意味着任何事情都可能发生在符合标准的实现中.真的没什么.(你的观点2是UB)

一个实现可以

  • 爆炸你的电脑并伤害你的身体
  • 制造一个吞噬整个太阳系的黑洞
  • 什么都不认真
  • 点亮键盘上的一些LED
  • 在你父母出生之前做一些时间旅行并杀死你所有的祖父母
  • 等等....

并符合(如果是UB); 阅读更多关于鼻子恶魔的想法.

因此,在UB上发生的事情是不可预测的,并且不可重复(通常).

更严重的是,想一想UB在连接到汽车ABS制动器或某些人工心脏或驱动某些核电站的计算机中意味着什么.

特别是,它有时可能会起作用.由于大多数操作系统都具有ASLR,因此您的代码很少有机会工作(例如,如果0xa31a3442碰巧指向某个有效位置,例如在堆栈上,但您不会在下次运行时重现该代码!)

UB是一种为实现者(例如编译器或操作系统)提供自由的方式,以及为计算机提供他们"想要"做任何事情的自由,换句话说就是不关心后果.这可以实现聪明的优化或很好的实现技巧.但应该关心(如果你正在编写飞机的嵌入式飞行控制系统,或者只是使用RasberryPi的一些hacky演示照明LED,或者在Linux上运行某些C++课程的简单示例),后果会有所不同.

回想一下,语言标准甚至不需要实现中的任何计算机(或任何硬件):您可能会与人类奴隶团队"运行"您的C++代码,但这将是非常不道德的(并且代价高昂且不可靠).

另请参见此处以获取更多参考.您至少应该阅读Lattner关于未定义行为的博客(他为C编写的大部分内容适用于C++和许多其他具有UB的语言).


(2015年12月和2016年6月新增)

NB.在Valgrind的工具和各种-fsanitize=调试选项近期GCC锵/ LLVM是相当有用的.此外,在编译器中启用所有警告和调试信息(例如g++ -Wall -Wextra -g),并使用适当的检测选项,如-fsanitize=undefined.请注意,在编译时无法在所有 UB情况下进行静态和详尽检测(这相当于停止问题).

PS.上面的答案并不特定于C++; 它也适合C!