C++ 限制未定义行为造成的混淆?

C++ 限制未定义行为造成的混淆?,c++,language-agnostic,undefined-behavior,C++,Language Agnostic,Undefined Behavior,正如我从阅读中了解到的,未定义的行为是编译器在编译时留下几个不相同的备选方案的结果。然而,这是否意味着,如果一个人遵循严格的编码实践(比如将每个赋值和每个等式放在一个单独的语句中,进行适当的调试和注释),那么在查找未定义行为的源时就不应该出现重大问题 此外,对于出现的每个错误,如果您识别代码,您应该知道哪些语句可以用于该特定语句,对吗 编辑:我对你写过你不想写的代码的地方不感兴趣。我对一些例子感兴趣,在这些例子中,用数学逻辑编写的代码不起作用 此外,我认为“好的编码实践”是每隔几行的有力的信息注

正如我从阅读中了解到的,未定义的行为是编译器在编译时留下几个不相同的备选方案的结果。然而,这是否意味着,如果一个人遵循严格的编码实践(比如将每个赋值和每个等式放在一个单独的语句中,进行适当的调试和注释),那么在查找未定义行为的源时就不应该出现重大问题

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

编辑:我对你写过你不想写的代码的地方不感兴趣。我对一些例子感兴趣,在这些例子中,用数学逻辑编写的代码不起作用


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

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

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


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

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

例如,以以下代码为例:

int arr[2];
arr[200] = 42;
这是未定义的行为。这并不是说编译器有多种选择。只是我所做的没有意义。理想情况下,首先不应该允许这样做,但是如果没有潜在的昂贵的运行时检查,我们不能保证这样的事情不会发生在我们的代码中。因此,在C++中,规则仅仅是语言只指定遵守规则的程序的行为。如果它在上面的例子中做了一些错误的事情,那么它只是没有定义应该发生什么

现在,想象一下你将如何检测这个错误。它将如何浮出水面?它似乎永远不会引起任何问题。也许我们只是碰巧写入了映射到进程的内存(这样我们就不会得到访问冲突),但从未被其他方式使用过(这样程序的其他部分就不会读取我们的垃圾值,或者覆盖我们所写的内容)。这样看来,这个程序是没有bug的,而且运行得很好

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

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

正如我从阅读中了解到的,未定义的行为是编译器在编译时留下几个不相同的备选方案的结果

虽然这可能是未定义行为的一个来源,但你说得太抽象了。您需要一个具体的示例来说明“编译时的非相同替代方案”的含义

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


<>请注意,导致“未定义行为”的代码仍然是合法C++代码。我认为它是一类代码或逻辑,应该非常自给自足地使用,当“未定义的行为”对于给定平台上给定程序的给定的实现是可预测的。您会发现,该语言认为“未定义的行为”实际上是针对特定环境/约束集定义的。

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

1.3.5实施定义的行为

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

1.3.12未定义的行为

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

1.3.13未指明的行为

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

尽管未指定的行为可以称为UB,但我从未见过,UB总是指未定义的行为。在整个标准中都有类似于“执行X是未定义的行为”的语句,但有时您会遇到一个根本没有涵盖的情况

换一种说法,如果您在任何地方有任何未定义的行为,那么所有赌注都将被取消。就标准而言,你的节目可以做任何事情,从邀请你的岳母参加超级杯周末到周末。由于UB的本质,您无法对其进行测试,也不能指望编译器提供任何帮助。(尽管对于一些琐碎的常见错误,编译器通常会生成诊断。)

通常将某些内容定义为UB是因为它在逻辑上没有意义(例如,访问超出边界的数组),但也常常是因为它需要实现执行太多的操作
std::string s = "abc";
char& c = s[0];
cout.write(s.data(), s.length());
c = '-';