限制未定义行为造成的混乱?

发布于 2024-08-17 16:59:44 字数 286 浏览 13 评论 0原文

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

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

编辑:我对您编写无意编写的代码的地方不感兴趣。我对数学逻辑合理的代码无法工作的例子感兴趣。

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

As I understand from my reading, undefined-behavior is the result of leaving the compiler with several non-identical alternatives at compile time. However, wouldn't that mean that if one were to follow strict coding practice (like putting each assignment and each equality in a separate statement, proper debugging and commenting) then it shouldn't pose a significant problem in finding the source of the undefined-behavior.

Further, there are, for each error that comes up, if you identify the code, you should know what statements can be used in that particular statement's stead, correct?

EDIT: I'm not interested in places where you have written code that you didn't mean to write. I'm interested in examples where code that is sound by mathematical logic fails to work.

Also, I consider 'good coding practice' to be strong informative comments every few lines, proper indentation, and debugging dumps on a regular basis.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

过期以后 2024-08-24 16:59:44

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

例如,采用以下代码:

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

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

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

或者它可能会到达一个甚至没有映射到我们的进程的地址。那么程序就会立即崩溃。

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

Undefined behavior isn't necessarily leaving the compiler with multiple alternatives. Most commonly it is simply doing something that doesn't make sense.

For example, take this code:

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

this is undefined behavior. It's not that the compiler was given multiple alternatives to choose from. it's just that what I'm doing does not make sense. Ideally, it should not be allowed in the first place, but without potentially expensive runtime checking, we can't guarantee that something like this won't occur in our code. So in C++, the rule is simply that the language specifies only the behavior of a program that sticks to the rules. If it does something erroneous like in the above example, it is simply undefined what should happen.

Now, imagine how you're going to detect this error. How is it going to surface? It might never seem to cause any problems. Perhaps we just so happen to write into memory that's mapped to the process (so we don't get an access violation), but is never otherwise used (so no other part of the program will read our garbage value, or overwrite what we wrote). Then it'll seem like the program is bug-free and works just fine.

Or it might hit an address that's not even mapped to our process. Then the program will crash immediately.

Or it might hit an address that's mapped to our process, but at some point later will be used for something. Then all we know is that sooner or later, the function reading from that address will get an unexpected value, and it'll behave weird. That part is easy to spot in the debugger, but it doesn't tell us anything about when or from where that garbage value was written. So there's no simple way to trace the error back to its source.

演出会有结束 2024-08-24 16:59:44

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

1.3.5 实现定义的行为

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

1.3.12 未定义行为

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

1.3.13 未指定行为

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

尽管未指定的行为可以被称为UB,但我从未见过这种情况,而UB总是意味着未定义的行为。整个标准中都有类似“做X是未定义行为”的陈述,”但有时您会遇到根本没有涵盖的案例。

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

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

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

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

看起来非常合理,对吧?不,这是 UB,但是它将在所有流行的实现上按照您的预期工作。

First, some definitions from the C++03 standard:

1.3.5 implementation-defined behavior

Behavior, for a well-formed program construct and correct data, that depends on the implementation and that each implementation shall document

1.3.12 undefined behavior

Behavior, such as might arises upon use of an erroneous program construct or erroneous data, for which this International Standard imposes no requirements. Undefined behavior may also be expected when this International Standard omits the description of any explicit definition or behavior.

1.3.13 unspecified behavior

Behavior, for a well-formed program construct and correct data, that depends on the implementation. The implementation is not required to document which behavior occurs.

Even though unspecified behavior could be called UB, I've never seen that, and UB always means undefined behavior. Throughout the standard are statements similar to "doing X is undefined behavior," but sometimes you run into a case that's simply not covered.

To put the definition another way, if you have any undefined behavior anywhere, then all bets are off. As far as the standard is concerned, your program could do anything from inviting your mother-in-law over for SuperBowl weekend to running nethack. Due to UB's very nature you can't test for it, and you can't expect any help from the compiler. (Though for some trivial, common errors compilers do generally produce diagnostics.)

Usually something is defined as UB because it just doesn't make sense logically (e.g. accessing an array out of bounds), but also often because it would require the implementation to do too much work to prevent—often at runtime. Remember C++ is derived from C, and being able to produce highly-optimized programs is a major goal of both languages. To this end, the languages defer to the programmer to make sure the code is correct in these situations, related to the "you don't pay for what you don't use" principle.

So, finally, UB is bad, very bad; avoid it at all costs. However, the hard part of UB isn't knowing what it is or under what circumstances it occurs; the hard part is recognizing when you invoke UB. For example:

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

Looks perfectly reasonable, right? Nope, this is UB, yet it will work as you expect on all the popular implementations.

意中人 2024-08-24 16:59:44

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

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

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

I'm not sure if there's a formal definition of "undefined behavior", but following good coding standards can reduce ambiguity and lead to fewer compile and runtime defects.

However, getting two programmers to agree on what "good coding standards" is a complicated and error-prone process.

To your second question, yes compilers will generally output an error code that you can use to fix the problem

简单 2024-08-24 16:59:44

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

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

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

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

As I understand from my reading, undefined-behavior is the result of leaving the compiler with several non-identical alternatives at compile time.

While that may be one source of undefined behavior, you're speaking too abstractly. You need a specific example of what you mean by "non-identical alternatives at compile time."

If by "follow strict coding practice," you mean don't use logic that results in undefined behavior, then yes (because there would be no undefined behavior). Tracking down a bug because of undefined behavior may or may not be easier than tracking one caused by a logic error.

Note that code which results in "undefined behavior" is still legal C++ code. I consider it a class of code/logic that should only very seldomly be used, when that "undefined behavior" is predictable for a given program on a given platform with a given implementation. You will find cases that what the language considers "undefined behavior" will in fact be defined for a particular environment/set of constraints.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文