5

正如我从阅读中了解到的那样,未定义行为是在编译时给编译器留下了几个不同的替代方案的结果。但是,这是否意味着如果要遵循严格的编码实践(例如将每个赋值和每个相等放在单独的语句中,适当的调试和注释),那么在查找未定义的来源时就不应该构成重大问题-行为。

此外,对于出现的每个错误,如果您识别代码,您应该知道可以使用哪些语句代替该特定语句,对吗?

编辑:我对你写了你不想写的代码的地方不感兴趣。我对数学逻辑合理的代码无法工作的示例感兴趣。

此外,我认为“良好的编码实践”是每隔几行提供信息丰富的注释、适当的缩进和定期调试转储。

4

4 回答 4

10

未定义的行为不一定会让编译器有多种选择。最常见的是,它只是做一些没有意义的事情。

例如,使用以下代码:

int arr[2];
arr[200] = 42;

这是未定义的行为。并不是说编译器有多种选择可供选择。只是我所做的没有意义。理想情况下,一开始就不应该允许它,但是如果没有潜在的昂贵的运行时检查,我们不能保证这样的事情不会在我们的代码中发生。所以在 C++ 中,规则就是语言只指定遵守规则的程序的行为。如果它在上面的例子中做了一些错误的事情,那么它只是未定义应该发生的事情。

现在,想象一下您将如何检测此错误。它将如何浮出水面?它似乎永远不会引起任何问题。也许我们只是碰巧写入映射到进程的内存(因此我们不会遇到访问冲突),但从未以其他方式使用(因此程序的其他部分不会读取我们的垃圾值,或覆盖我们编写的内容)。然后看起来该程序没有错误并且运行良好。

或者它可能会命中一个甚至没有映射到我们的进程的地址。然后程序将立即崩溃。

或者它可能会命中一个映射到我们进程的地址,但稍后将用于某些事情。那么我们所知道的是,从那个地址读取的函数迟早会得到一个意想不到的值,它的行为会很奇怪。这部分很容易在调试器中发现,但它并没有告诉我们关于何时或从何处写入垃圾值的任何信息。因此,没有简单的方法可以将错误追溯到其源头。

于 2010-01-12T05:58:44.717 回答
7

首先,来自 C++03 标准的一些定义:

1.3.5 实现定义的行为

行为,对于格式良好的程序构造和正确的数据,取决于实现并且每个实现都应记录

1.3.12 未定义的行为

行为,例如在使用错误程序结构或错误数据时可能出现的行为,本国际标准对此没有任何要求。当本国际标准省略任何明确定义或行为的描述时,也可能出现未定义行为。

1.3.13 未指明的行为

行为,对于格式良好的程序构造和正确的数据,取决于实现。实现不需要记录发生了哪些行为。

尽管可以将未指定的行为称为 UB,但我从未见过,而且 UB 始终意味着未定义的行为。 贯穿整个标准的语句类似于“执行 X 是未定义的行为”,但有时您会遇到根本未涵盖的情况。

换一种说法,如果你在任何地方有任何未定义的行为,那么所有的赌注都是 off。就标准而言,您的程序可以做任何事情,从邀请您的岳母参加 SuperBowl 周末到运行 nethack。由于 UB 的本质,您无法对其进行测试,并且您不能期望编译器提供任何帮助。(尽管对于一些微不足道的常见错误,编译器通常会产生诊断。)

通常某些东西被定义为 UB,因为它在逻辑上没有意义(例如访问越界数组),但也经常因为它需要实现做太多工作来防止 - 通常在运行时。请记住,C++ 源自 C,能够生成高度优化的程序是这两种语言的主要目标。为此,语言遵从程序员的要求,以确保代码在这些情况下是正确的,这与“你不为你不使用的东西付费”的原则有关。

所以,最后,UB 很糟糕,非常糟糕;不惜一切代价避免它。但是,UB 的难点在于不知道它是什么或在什么情况下发生。当您调用 UB 时,困难的部分是识别。例如:

std::string s = "abc";
char& c = s[0];
cout.write(s.data(), s.length());
c = '-';

看起来完全合理,对吧?不,这是 UB,但它会在所有流行的实现中按您期望的那样工作。

于 2010-01-12T06:38:00.270 回答
1

我不确定是否有“未定义行为”的正式定义,但遵循良好的编码标准可以减少歧义并减少编译和运行时缺陷。

然而,让两个程序员就“好的编码标准”达成一致是一个复杂且容易出错的过程。

对于您的第二个问题,是的,编译器通常会输出一个错误代码,您可以使用它来解决问题

于 2010-01-12T05:58:41.770 回答
1

正如我从阅读中了解到的那样,未定义行为是在编译时给编译器留下了几个不同的替代方案的结果。

虽然这可能是未定义行为的来源之一,但您说得太抽象了。您需要一个具体示例来说明“编译时的不同选择”是什么意思。

如果通过“遵循严格的编码实践”,您的意思是不要使用导致未定义行为的逻辑,那么是的(因为不会有未定义的行为)。由于未定义的行为而跟踪错误可能会也可能不会比跟踪由逻辑错误引起的错误更容易。

请注意,导致“未定义行为”的代码仍然是合法的 C++ 代码。我认为它是一类很少使用的代码/逻辑,当“未定义的行为”对于给定平台上的给定程序具有给定实现是可预测的。您会发现语言认为的“未定义行为”实际上将针对特定环境/一组约束进行定义。

于 2010-01-12T06:01:37.193 回答