限制由未定义行为引起的混乱?

时间:2010-01-12 05:49:10

标签: c++ language-agnostic undefined-behavior

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

此外,对于每个出现的错误,如果您确定代码,您应该知道在该特定语句中可以使用哪些语句,是否正确?

编辑:我对你编写的代码并不感兴趣。我对通过数学逻辑发声的代码无法工作的例子感兴趣。

此外,我认为“良好的编码实践”是每隔几行,正确的缩进和调试转储的强大信息性评论。

4 个答案:

答案 0 :(得分:10)

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

例如,请使用以下代码:

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

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

现在,想象一下你将如何检测这个错误。它是如何浮出水面的?它可能从不似乎会导致任何问题。也许我们只是碰巧写入映射到进程的内存(所以我们没有得到访问冲突),但从来没有使用过(因此程序的其他部分都不会读取我们的垃圾值,或者覆盖我们写的内容) )。然后它看起来像程序没有错误,并且运行正常。

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

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

答案 1 :(得分:7)

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

  

1.3.5实施定义的行为

     

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

     

1.3.12未定义的行为

     

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

     

1.3.13未指明的行为

     

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

即使未指定的行为可以被称为UB,我也从未见过,UB总是意味着未定义的行为。整个标准都是类似于“做X”的语句是未定义的行为,“但有时你遇到一个根本没有被覆盖的案例。

以另一种方式定义,如果您在任何地方有任何未定义的行为,那么所有投注都将关闭。就标准而言,您的计划可以做任何事情,从邀请您的岳母到SuperBowl周末到running nethack。由于UB的本质,你不能测试它,你不能指望编译器的任何帮助。 (虽然对于一些微不足道的常见错误,编译器通常会产生诊断。)

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

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

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

看起来非常合理,对吗?不,这是UB,但它可以像你期望的那样在所有流行的实现中运行。

答案 2 :(得分:1)

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

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

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

答案 3 :(得分:1)

  

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

虽然这可能是一个未定义行为的来源,但你说得过于抽象。您需要一个特定的例子,说明“在编译时不相同的替代方案。”

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

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