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

Is segmentation fault actual undefined behavior when we refer to a non-static data-member

本文关键字:未定义 错误 分段 引用 我们 静态 数据成员      更新时间:2023-10-16

我已经阅读了下面的规则,我一直在尝试写一个例子,它反映了一个规则。规则来自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
}

//1形成良好且不引起任何UB的情况下,但//2产生分段故障,即UB,这是真的吗?

未定义的行为意味着任何都可以通过符合标准的实施来实现。真的什么都可以。(你的第2点是UB)

的实施

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

并符合(在UB的情况下);阅读更多关于鼻妖的熟悉概念。

因此,UB上发生的事情是不可预测的,也是不可复制的(一般来说)。

更认真地说,想想UB在连接汽车ABS制动器的计算机中,或者在一些人造心脏中,或者驾驶一些核电站中可能意味着什么

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

UB是一种让实现者(例如编译器或操作系统)和计算机自由地做他们"想做的"事情的方式,换句话说,就是不在乎后果。这可以实现例如巧妙的优化或良好的实现技巧。但是应该注意(如果你正在对飞机的嵌入式飞行控制系统进行编码,或者只是用RasberryPi对一些古怪的演示照明LED进行编码,或是在Linux上运行的一些C++课程的一个简单示例,结果会有所不同)。

回想一下,语言标准在实现过程中甚至不需要任何计算机(或任何硬件):你可能会和一组人类奴隶一起"运行"C++代码,但这将是非常不道德的(成本高昂且不可靠)。

有关更多参考资料,请参阅此处。你至少应该阅读Lattner在Undefined Behavior上的博客(他为C编写的大部分内容适用于C++和许多其他具有UB的语言)。


(2015年12月和2016年6月添加)

注:。valgrind工具和最近GCC或Clang/LLVM的各种-fsanitize=调试选项非常有用。此外,在编译器中启用所有警告和调试信息(例如g++ -Wall -Wextra -g),并使用适当的检测选项(如-fsanitize=undefined)。请注意,在编译时不可能静态地、详尽地检测所有的UB情况(这相当于Halting Problem)。

PS。上面的答案并不是C++特有的;它也适用于C

您问:

在这种情况下,//1是正确的,不会引起任何UB吗?

你引用的标准中没有提到任何内容

你还问:

但是//2产生了分段故障,哪一个是UB?

您引用的标准部分与此特定行为不一致。你看到UB是因为p点在哪里。它指向不包含有效对象的内存。

规则3.8/5是关于在对象的构造/销毁之外,但在对象所在的内存的分配/释放内部的时间。以下演示了对象生命周期之外的点:

void *buffer = malloc(sizeof(A));
// outside of lifetime of a
// a->b is undefined
A* a = new (buffer) A();
// within lifetime of a
// a->b is valid
a->~A();
// outside of lifetime of a
// a->b is undefined
free(buffer);

从技术上讲,你的帖子实际上并没有反映规则3.8/5,因为你在对象的生命周期之外没有访问它。您只是将随机内存作为一个实例。