未定义,未指定和实施定义的行为
C和C ++中的不确定的行为(ub)? 未指定的行为和实现定义行为呢?它们有什么区别?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
C和C ++中的不确定的行为(ub)? 未指定的行为和实现定义行为呢?它们有什么区别?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(10)
实施定义 -
未指定的 -
未定义 -
Implementation defined-
Unspecified -
Undefined-
从历史上看,实施定义的行为和不确定的行为代表了标准的作者期望,他们撰写质量实施的人们将使用判断来确定哪些行为保证(如果有的话)对在该计划中运行的计划字段中有用是有用的。预期目标。高端数字处理代码的需求与低级系统代码的需求完全不同,UB和IDB都使编译器作家灵活地满足这些不同的需求。既不要求任何类别的实施方式以任何特定目的有用的方式,甚至对任何目的有用。但是,声称适合特定目的的质量实施应以适合此目的的方式行事是否需要标准。
实施定义的行为和未定义的行为之间的唯一区别在于,前者要求实施定义和记录一致的行为,即使在实施不可能使用的情况下,实现行为也是有用的。它们之间的分界线不是通常对于实现行为是否有用(编译器作者应定义有用的行为,而当标准是否要求它们是否要求它们时),而是),而是 是否可能存在定义行为的实现同时成本高昂,无用。关于这种实现可能存在的判断并非以任何方式,形状或形式,这意味着关于支持其他平台上确定行为的有用性的任何判断。
不幸的是,自1990年代中期以来,编译器作家已经开始将缺乏行为命令解释为判断力,即即使在其重要的应用领域,甚至在他们几乎没有成本的系统中,行为保证也不值得成本。编译器作家并没有将UB视为行使合理判断的邀请,而是开始将其视为借口不是。
例如,给定以下代码:
两个组合的实现将不必花费任何努力
任何处理表达式
v<< POW
作为两个组合班不考虑
v
是正面还是负面。但是
V
的of。即使对每个意义上的编译器都支持负值的左移动,并且大量现有代码依赖于这种行为,现代哲学也会解释这样一个事实,即标准表明左移动负值是UB暗示编译器作家应该随意忽略这一点。Historically, both Implementation-Defined Behavior and Undefined Behavior represented situations in which the authors of the Standard expected that people writing quality implementations would use judgment to decide what behavioral guarantees, if any, would be useful for programs in the intended application field running on the intended targets. The needs of high-end number-crunching code are quite different from those of low-level systems code, and both UB and IDB give compiler writers flexibility to meet those different needs. Neither category mandates that implementations behave in a way that's useful for any particular purpose, or even for any purpose whatsoever. Quality implementations that claim to be suitable for a particular purpose, however, should behave in a manner befitting such purpose whether the Standard requires it or not.
The only difference between Implementation-Defined Behavior and Undefined Behavior is that the former requires that implementations define and document a consistent behavior even in cases where nothing the implementation could possibly do would be useful. The dividing line between them is not whether it would generally be useful for implementations to define behaviors (compiler writers should define useful behaviors when practical whether the Standard requires them to or not) but whether there might be implementations where defining a behavior would be simultaneously costly and useless. A judgment that such implementations might exist does not in any way, shape, or form, imply any judgment about the usefulness of supporting a defined behavior on other platforms.
Unfortunately, since the mid 1990s compiler writers have started to interpret the lack of behavioral mandates as an judgment that behavioral guarantees aren't worth the cost even in application fields where they're vital, and even on systems where they cost practically nothing. Instead of treating UB as an invitation to exercise reasonable judgment, compiler writers have started treating it as an excuse not to do so.
For example, given the following code:
a two's-complement implementation would not have to expend any effort
whatsoever to treat the expression
v << pow
as a two's-complement shiftwithout regard for whether
v
was positive or negative.The preferred philosophy among some of today's compiler writers, however, would suggest that because
v
can only be negative if the program is going to engage in Undefined Behavior, there's no reason to have the program clip the negative range ofv
. Even though left-shifting of negative values used to be supported on every single compiler of significance, and a large amount of existing code relies upon that behavior, modern philosophy would interpret the fact that the Standard says that left-shifting negative values is UB as implying that compiler writers should feel free to ignore that.C ++标准N3337 § 1.3.10
实施定义的行为
有时C ++标准不会在某些构造上强加特定的行为,而是说必须选择特定的,定义明确的行为,并通过特定的实现(版本图书馆)。因此,用户仍然可以确切地知道程序的行为,即使标准没有描述这一点。
C ++标准N3337 § 1.3.24
未定义的行为
当程序遇到未根据C ++标准定义的构造时,允许执行您想做的任何事情(也许向我发送电子邮件或向您发送电子邮件或完全忽略代码)。
C ++标准N3337 § 1.3.25
未指定的行为
C ++标准不会在某些构造上强加特定的行为,而是说必须通过特定实现(库的版本)选择特定的,定义明确的行为(但不一定描述)。因此,在没有提供描述的情况下,用户很难确切知道程序的行为。
C++ standard n3337 § 1.3.10
implementation-defined behavior
Sometimes C++ Standard doesn't impose particular behavior on some constructs but says instead that a particular, well defined behavior has to be chosen and described by particular implementation (version of library). So user can still know exactly how will program behave even though Standard doesn't describe this.
C++ standard n3337 § 1.3.24
undefined behavior
When the program encounters construct that is not defined according to C++ Standard it is allowed to do whatever it wants to do ( maybe send an email to me or maybe send an email to you or maybe ignore the code completely).
C++ standard n3337 § 1.3.25
unspecified behavior
C++ Standard doesn't impose particular behavior on some constructs but says instead that a particular, well-defined behavior has to be chosen ( but not necessarily described) by particular implementation (version of library). So in the case when no description has been provided it can be difficult for the user to know exactly how the program will behave.
未定义的行为是丑陋的 - 如“好,坏和丑陋”。
良好:出于正确的理由,可以编译和工作的程序。
不好:一个具有错误的程序,该程序可以检测和投诉。
丑陋:一个有错误的程序,编译器无法检测和警告,这意味着该程序编译了,并且似乎在某些时候可以正常工作,但也使某些时间失败了。那就是不确定的行为。
某些程序语言和其他正式系统会努力限制“不确定的湾” - 也就是说,他们尝试安排事情,以便大多数或所有程序都是“好”或“坏”,而很少有人是“丑陋的” ”。然而,C的特征是它的“不确定鸿沟”非常广泛。
Undefined behavior is ugly -- as in, "The good, the bad, and the ugly".
Good: a program that compiles and works, for the right reasons.
Bad: a program that has an error, of a kind that the compiler can detect and complain about.
Ugly: a program that has an error, that the compiler cannot detect and warn about, meaning that the program compiles, and may seem to work correctly some of the time, but also fails bizarrely some of the time. That's what undefined behavior is.
Some program languages and other formal systems try hard to limit the "gulf of undefinedness" -- that is, they try to arrange things so that most or all programs are either "good" or "bad", and that very few are "ugly". It's a characteristic feature of C, however, that its "gulf of undefinedness" is quite wide.
更新2024:
对于C ++,由于C ++ 26,我们还具有错误的行为,它与不确定的行为不同。
这篇文章的讨论篇幅:什么是错误的”>行为?它与未定义的行为有何不同?。
但是,由于当前的帖子是各种“有问题”的律师的主要文章,
我认为在此处发布摘要将很有帮助:
[[不确定]]
属性来保持Pre-C ++ 26行为。描述来自 cppreference :
最后,这篇文章可能是Intereset的:。
据此,编译器确实应确保某种初始化。
由于具有性能效果,因此您可以使用
[[不确定]]
属性如上所述避免。Update 2024:
For C++ Since C++26 we also have erroneous behavior which is different than undefined-behavior.
This post discusses it in length: What is erroneous behavior? How is it different from undefined behavior?.
However since the current post is the main one regarding all sorts of "problematic" behviors,
I thought it would be helpful to post a summary here for completeness:
[[indeterminate]]
attribute.The description from cppreference:
Finally this post might be of intereset: In C++26, are implementations required to "initialize" uninitialized variables to some fixed byte pattern?.
According to it the compiler should indeed ensure some kind of initialization.
Since this has a performance effect, you can use the
[[indeterminate]]
attribute as mentioned above to avoid it.未定义的行为 是C和C ++语言的这些方面之一,这对于来自其他语言的程序员来说可能会令人惊讶(其他语言试图更好地隐藏它)。基本上,即使许多C ++编译器不会报告程序中的任何错误,也可以编写无法以可预测方式行为的C ++程序!
让我们看一个经典示例:
变量
p
指向字符串文字“ hello!\ n”
,下面的两个作业尝试修改该字符串字面的字体。这个程序做什么?根据 c ++标准,[lex.string]注释4 ,它调用未定义的行为:我可以听到人们尖叫的声音。尝试导致核心转储”。这正是不确定行为的问题。基本上,该标准允许一旦调用未定义的行为(甚至鼻恶魔),任何事情都会发生。如果根据您的语言心理模型存在“正确”的行为,则该模型简直是错误的; C ++标准是唯一的投票期。
未定义行为的其他示例包括
[Into.defs] 也定义了未定义的两个较不危险的兄弟,未指定的兄弟行为和实施定义的行为:
您该怎么做才能避免遇到未定义的行为?基本上,您必须阅读良好的C ++书籍由知道他们在说什么的作者。避免互联网教程。避免公牛。
Undefined behavior is one of those aspects of the C and C++ language that can be surprising to programmers coming from other languages (other languages try to hide it better). Basically, it is possible to write C++ programs that do not behave in a predictable way, even though many C++ compilers will not report any errors in the program!
Let's look at a classic example:
The variable
p
points to the string literal"hello!\n"
, and the two assignments below try to modify that string literal. What does this program do? According to the C++ standard, [lex.string] note 4, it invokes undefined behavior:I can hear people screaming "But wait, I can compile this no problem and get the output
yellow
" or "What do you mean undefined, string literals are stored in read-only memory, so the first assignment attempt results in a core dump". This is exactly the problem with undefined behavior. Basically, the standard allows anything to happen once you invoke undefined behavior (even nasal demons). If there is a "correct" behavior according to your mental model of the language, that model is simply wrong; The C++ standard has the only vote, period.Other examples of undefined behavior include
i++ + ++i
.[intro.defs] also defines undefined behavior's two less dangerous brothers, unspecified behavior and implementation-defined behavior:
What can you do to avoid running into undefined behavior? Basically, you have to read good C++ books by authors who know what they're talking about. Avoid internet tutorials. Avoid bullschildt.
好吧,这基本上是 c Standard ::
Well, this is basically a straight copy-paste from the C standard:
也许比标准的严格定义更容易理解简单的措辞。
实施定义的行为:
该语言说我们有数据类型。编译器供应商指定了他们使用的尺寸,并提供了有关他们所做工作的文档。
未定义的行为:
你做错了什么。例如,您在
int
中具有很大的价值,该价值不适合char
。您如何将该值放入char
中?实际上,没有办法!任何事情都可能发生,但是最明智的事情就是将其第一个字节放入char
中。分配第一个字节是错误的,但这就是引擎盖下发生的情况。未指定的行为:
首先执行这两个功能中的哪个?
该语言未指定评估,左至右或向左!因此,未指定的行为可能会或可能不会导致不确定的行为,但是当然,您的程序不应产生未指定的行为。
@eskay我认为您的问题值得编辑答案以澄清更多:)
实施定义和未指定之间的区别在于,编译器应该在第一种情况下选择行为,但在第二种情况下不必这样做。例如,实现必须具有
sizeof(int)
的一个定义。因此,它不能说该程序的某些部分 为4,而其他部分为8。与未指定的行为不同,编译器可以说:“好吧,我将从左到右评估这些论点,并评估了下一个函数的论点。”它可以在同一程序中发生,这就是为什么它被称为 未指定的 。实际上,如果指定了一些未指定的行为,则可以使C ++更容易。在此处查看 dr。 Stroustrup的答案:Maybe simpler wording could be easier to understand than the rigorous definition of the standards.
implementation-defined behavior:
The language says that we have data-types. The compiler vendors specify what sizes shall they use, and provide a documentation of what they did.
undefined behavior:
You are doing something wrong. For example, you have a very large value in an
int
that doesn't fit inchar
. How do you put that value inchar
? actually there is no way! Anything could happen, but the most sensible thing would be to take the first byte of that int and put it inchar
. It is just wrong to do that to assign the first byte, but thats what happens under the hood.unspecified behavior:
Which of these two functions is executed first?
The language doesn't specify the evaluation, left to right or right to left! So an unspecified behavior may or mayn't result in an undefined behavior, but certainly your program should not produce an unspecified behavior.
@eSKay I think your question is worth editing the answer to clarify more :)
The difference between implementation-defined and unspecified, is that the compiler is supposed to pick a behavior in the first case but it doesn't have to in the second case. For example, an implementation must have one and only one definition of
sizeof(int)
. So, it can't say thatsizeof(int)
is 4 for some portion of the program and 8 for others. Unlike unspecified behavior, where the compiler can say: "OK I am gonna evaluate these arguments left-to-right and the next function's arguments are evaluated right-to-left." It can happen in the same program, that's why it is called unspecified. In fact, C++ could have been made easier if some of the unspecified behaviors were specified. Take a look here at Dr. Stroustrup's answer for that:从官方的基本原理文件中
From the official C Rationale Document
不确定的行为与未指定的行为有一个简短的描述。
他们的最后摘要:
Undefined Behavior vs. Unspecified Behavior has a short description of it.
Their final summary: