C++ 未定义、未指定和实现定义的行为

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2397984/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 23:20:51  来源:igfitidea点击:

Undefined, unspecified and implementation-defined behavior

c++cundefined-behaviorunspecified-behaviorimplementation-defined-behavior

提问by Zolomon

What is undefined behavior in C and C++? What about unspecified behavior and implementation-defined behavior? What is the difference between them?

C 和 C++ 中的未定义行为是什么?未指定的行为和实现定义的行为呢?它们之间有什么区别?

采纳答案by fredoverflow

Undefined behavioris 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!

未定义行为是 C 和 C++ 语言的其中一个方面,可能会让来自其他语言的程序员感到惊讶(其他语言试图更好地隐藏它)。基本上,即使许多 C++ 编译器不会报告程序中的任何错误,也可以编写不以可预测方式运行的 C++ 程序!

Let's look at a classic example:

我们来看一个经典的例子:

#include <iostream>

int main()
{
    char* p = "hello!\n";   // yes I know, deprecated conversion
    p[0] = 'y';
    p[5] = 'w';
    std::cout << p;
}

The variable ppoints to the string literal "hello!\n", and the two assignments below try to modify that string literal. What does this program do? According to section 2.14.5 paragraph 11 of the C++ standard, it invokes undefined behavior:

该变量p指向字符串文字"hello!\n",下面的两个赋值尝试修改该字符串文字。这个程序有什么作用?根据 C++ 标准的第 2.14.5 节第 11 段,它调用未定义的行为

The effect of attempting to modify a string literal is undefined.

尝试修改字符串文字的效果是未定义的。

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.

我可以听到人们尖叫“但是等等,我可以编译这个没问题并获得输出yellow”或“你是什么意思未定义,字符串文字存储在只读内存中,因此第一次分配尝试导致核心转储”。这正是未定义行为的问题。基本上,一旦您调用未定义的行为(甚至是鼻恶魔),该标准就会允许任何事情发生。如果根据您的语言心理模型存在“正确”行为,那么该模型就是错误的;C++ 标准拥有唯一的投票权。

Other examples of undefined behavior include accessing an array beyond its bounds, dereferencing the null pointer, accessing objects after their lifetime endedor writing allegedly clever expressionslike i++ + ++i.

未定义行为的其他例子包括访问超出其边界的数组,解引用空指针对象访问他们的一生结束后或写据称聪明的表情一样i++ + ++i

Section 1.9 of the C++ standard also mentions undefined behavior's two less dangerous brothers, unspecified behaviorand implementation-defined behavior:

C++ 标准的第 1.9 节还提到了未定义行为的两个不太危险的兄弟,未指定行为实现定义行为

The semantic descriptions in this International Standard define a parameterized nondeterministic abstract machine.

Certain aspects and operations of the abstract machine are described in this International Standard as implementation-defined(for example, sizeof(int)). These constitute the parameters of the abstract machine. Each implementation shall include documentation describing its characteristics and behavior in these respects.

Certain other aspects and operations of the abstract machine are described in this International Standard as unspecified(for example, order of evaluation of arguments to a function). Where possible, this International Standard defines a set of allowable behaviors. These define the nondeterministic aspects of the abstract machine.

Certain other operations are described in this International Standard as undefined(for example, the effect of dereferencing the null pointer). [ Note: this International Standard imposes no requirements on the behavior of programs that contain undefined behavior.end note]

本国际标准中的语义描述定义了一个参数化的非确定性抽象机器。

抽象机的某些方面和操作在本国际标准中被描述为实现定义(例如,sizeof(int))。这些构成了抽象机的参数。每个实现都应包括描述其在这些方面的特性和行为的文档。

抽象机的某些其他方面和操作在本国际标准中被描述为未指定(例如,函数参数的评估顺序)。在可能的情况下,本国际标准定义了一组允许的行为。这些定义了抽象机器的非确定性方面。

本国际标准中将某些其他操作描述为未定义(例如,取消引用空指针的效果)。[注意本国际标准对包含未定义行为的程序的行为没有要求。尾注]

Specifically, section 1.3.24 states:

具体来说,第 1.3.24 节指出:

Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

允许的未定义行为的范围从完全忽略具有不可预测结果的情况,在翻译或程序执行期间以环境特征的文件化方式(有或没有发布诊断消息),到终止翻译或执行(通过发布诊断消息)。

What can you do to avoid running into undefined behavior? Basically, you have to read good C++ booksby authors who know what they're talking about. Screw internet tutorials. Screw bullschildt.

你能做些什么来避免遇到未定义的行为?基本上,您必须阅读了解自己在说什么的作者的优秀 C++ 书籍。拧互联网教程。螺丝牛角。

回答by AnT

Well, this is basically a straight copy-paste from the standard

嗯,这基本上是标准的直接复制粘贴

3.4.11 implementation-defined behaviorunspecified behavior where each implementation documents how the choice is made

2 EXAMPLE An example of implementation-defined behavior is the propagation of the high-order bit when a signed integer is shifted right.

3.4.31 undefined behaviorbehavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements

2 NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

3 EXAMPLE An example of undefined behavior is the behavior on integer overflow.

3.4.41 unspecified behavioruse of an unspecified value, or other behavior where this International Standard provides two or more possibilities and imposes no further requirements on which is chosen in any instance

2 EXAMPLE An example of unspecified behavior is the order in which the arguments to a function are evaluated.

3.4.11 implementation-defined behavior未指定的行为,其中每个实现都记录了如何做出选择

2 示例 实现定义行为的一个示例是当有符号整数右移时高位的传播。

3.4.31未定义的行为行为,在使用不可移植或错误的程序结构或错误数据时,本国际标准对此没有要求

2 注意 可能的未定义行为范围从完全忽略情况并产生不可预测的结果,在翻译或程序执行期间以环境特征的文件化方式(有或没有发布诊断消息),到终止翻译或执行(有诊断消息的发布)。

3 示例 未定义行为的一个示例是整数溢出行为。

3.4.41 unspecified behavior未规定值的使用,或本国际标准提供了两种或多种可能性并且在任何情况下都没有对其施加进一步要求的其他行为

2 示例 未指定行为的一个示例是评估函数参数的顺序。

回答by AraK

Maybe easy wording could be easier for understanding 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 intthat doesn't fit in char. How do you put that value in char? 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 in char. It is just wrong to do that to assign the first byte, but thats what happens under the hood.

未定义的行为
你做错了什么。例如,您在int不适合char. 你怎么把这个值放进去char?其实没有办法!任何事情都可能发生,但最明智的做法是获取该 int 的第一个字节并将其放入char. 这样做分配第一个字节是错误的,但这就是幕后发生的事情。

unspecified behavior
Which function of these two is executed first?

未指明的行为
这两个函数中的哪个函数先执行?

void fun(int n, int m);

int fun1()
{
  cout << "fun1";
  return 1;
}
int fun2()
{
  cout << "fun2";
  return 2;
}
...
fun(fun1(), fun2()); // which one 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 :)

@eSKay 我认为你的问题值得编辑答案以澄清更多:)

for fun(fun1(), fun2());isn't the behaviour "implementation defined"? The compiler has to choose one or the other course, after all?

因为fun(fun1(), fun2());行为不是“实现定义”吗?毕竟,编译器必须选择一个或另一个过程?

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 that sizeof(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:

实现定义和未指定之间的区别在于,编译器应该在第一种情况下选择一种行为,但在第二种情况下则不必。例如,一个实现必须有一个且只有一个 的定义sizeof(int)。因此,不能说sizeof(int)程序的某些部分是 4,其他部分是 8。与未指定的行为不同,编译器可以说 OK 我将从左到右计算这些参数,而从右到左计算下一个函数的参数。它可以发生在同一个程序中,这就是为什么它被称为unspecified。事实上,如果指定了一些未指定的行为,C++ 本来可以变得更容易。在此处查看Stroustrup 博士对此的回答

It is claimed that the difference between what can be produced giving the compiler this freedom and requiring "ordinary left-to-right evaluation" can be significant. I'm unconvinced, but with innumerable compilers "out there" taking advantage of the freedom and some people passionately defending that freedom, a change would be difficult and could take decades to penetrate to the distant corners of the C and C++ worlds. I am disappointed that not all compilers warn against code such as ++i+i++. Similarly, the order of evaluation of arguments is unspecified.

IMO far too many "things" are left undefined, unspecified, implementation-defined, etc. However, that's easy to say and even to give examples of, but hard to fix. It should also be noted that it is not all that difficult to avoid most of the problems and produce portable code.

据称,可以产生什么给编译器这种自由与需要“普通的从左到右评估”之间的差异可能很大。我不相信,但随着无数编译器“在那里”利用自由以及一些人热情地捍卫这种自由,改变将是困难的,可能需要几十年才能渗透到 C 和 C++ 世界的遥远角落。令我失望的是,并非所有编译器都会针对诸如 ++i+i++ 之类的代码发出警告。同样,未指定参数的评估顺序。

IMO 太多的“事物”未定义、未指定、实现定义等。然而,这说起来容易,甚至可以举出例子,但很难解决。还应该注意的是,避免大多数问题并生成可移植代码并不是那么困难。

回答by Johannes Schaub - litb

From the official C Rationale Document

来自官方的 C 基本原理文档

The terms unspecifiedbehavior, undefinedbehavior, and implementation-definedbehavior are used to categorize the result of writing programs whose properties the Standard does not, or cannot, completely describe. The goal of adopting this categorization is to allow a certain variety among implementations which permits quality of implementation to be an active force in the marketplace as well as to allow certain popular extensions, without removing the cachet of conformance to the Standard. Appendix F to the Standard catalogs those behaviors which fall into one of these three categories.

Unspecified behaviorgives the implementor some latitude in translating programs. This latitude does not extend as far as failing to translate the program.

Undefined behaviorgives the implementor license not to catch certain program errors that are difficult to diagnose. It also identifies areas of possible conforming language extension: the implementor may augment the language by providing a definition of the officially undefined behavior.

Implementation-definedbehavior gives an implementor the freedom to choose the appropriate approach, but requires that this choice be explained to the user. Behaviors designated as implementation-defined are generally those in which a user could make meaningful coding decisions based on the implementation definition. Implementors should bear in mind this criterion when deciding how extensive an implementation definition ought to be. As with unspecified behavior, simply failing to translate the source containing the implementation-defined behavior is not an adequate response.

术语未指定的行为、未定义的行为和实现定义的行为用于对编写程序的结果进行分类,这些程序的属性标准没有或不能完全描述。采用这种分类的目标是允许实现之间的某种多样性,从而允许实现质量成为市场上的积极力量,并允许某些流行的扩展,而不会消除符合标准的标志。标准的附录 F 列出了属于这三个类别之一的行为。

未指定的行为给了实现者一些翻译程序的自由。这个范围不会延伸到无法翻译程序。

未定义的行为给予实施者不捕捉某些难以诊断的程序错误的许可。它还确定了可能符合语言扩展的领域:实现者可以通过提供官方未定义行为的定义来扩充语言。

实现定义的行为使实现者可以自由选择合适的方法,但要求向用户解释这个选择。指定为实现定义的行为通常是用户可以根据实现定义做出有意义的编码决策的行为。实现者在决定实现定义的范围时应该牢记这个标准。与未指定的行为一样,仅仅未能翻译包含实现定义行为的源代码并不是一个适当的响应。

回答by Anders Abel

Undefined Behavior vs. Unspecified Behaviorhas a short description of it.

Undefined Behavior vs. Unspecified Behavior有一个简短的描述。

Their final summary:

他们的最终总结:

To sum up, unspecified behavior is usually something you shouldn't worry about, unless your software is required to be portable. Conversely, undefined behavior is always undesirable and should never occur.

总而言之,除非您的软件需要可移植,否则通常您不必担心未指定的行为。相反,未定义的行为总是不可取的,永远不应该发生。

回答by supercat

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.

从历史上看,实现定义的行为和未定义的行为都代表了标准作者期望编写高质量实现的人会使用判断来决定哪些行为保证(如果有的话)对运行在目标应用程序领域中的程序有用的情况。预期目标。高端数字运算代码的需求与低级系统代码的需求大不相同,UB 和 IDB 都为编译器编写者提供了满足这些不同需求的灵活性。这两个类别都没有要求实现的行为方式对任何特定目的或什至任何目的都有用。然而,声称适用于特定目的的质量实现应该以适合该目的的方式运行标准是否要求

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 notto do so.

不幸的是,自 1990 年代中期以来,编译器编写者开始将缺乏行为指令解释为判断行为保证不值得付出代价,即使在它们至关重要的应用领域,甚至在它们几乎没有成本的系统上也是如此。编译器作者并没有将 UB 视为进行合理判断的邀请,而是开始将其视为这样做的借口。

For example, given the following code:

例如,给定以下代码:

int scaled_velocity(int v, unsigned char pow)
{
  if (v > 250)
    v = 250;
  if (v < -250)
    v = -250;
  return v << pow;
}

a two's-complement implementation would not have to expend any effort whatsoever to treat the expression v << powas a two's-complement shift without regard for whether vwas positive or negative.

二进制补码实现不必花费任何精力来将表达式v << pow视为二进制补码转换,而不考虑v是正数还是负数。

The preferred philosophy among some of today's compiler writers, however, would suggest that because vcan 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 of v. 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.

然而,当今一些编译器作者的首选哲学表明,因为v只有在程序将要从事未定义行为时才能为负数,所以没有理由让程序将v. 尽管过去每个重要的编译器都支持负值左移,并且大量现有代码依赖于这种行为,但现代哲学会解释标准说左移负值是 UB 的事实暗示编译器作者应该随意忽略它。

回答by 4pie0

C++ standard n3337 §1.3.10 implementation-defined behavior

C++ 标准 n3337 §1.3.10 实现定义的行为

behavior, for a well-formed program construct and correct data, that depends on the implementation and that each implementation documents

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

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 describedby particular implementation (version of library). So user can still know exactly how will program behave even though Standard doesn't describe this.

有时候,C ++标准并没有对一些特定结构的行为,而且说而不是特定的,定义明确的行为必须选择和描述的特定实现(库的版本)。所以即使标准没有描述这一点,用户仍然可以确切地知道程序将如何运行。



C++ standard n3337 §1.3.24 undefined behavior

C++ 标准 n3337 §1.3.24 未定义行为

behavior for which this International Standard imposes no requirements [ Note: Undefined behavior may be expected when this International Standard omits any explicit definition of behavior or when a program uses an erroneous construct or erroneous data. Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message). Many erroneous program constructs do not engender undefined behavior; they are required to be diagnosed. — end note ]

本国际标准未对其施加要求的行为 [注意:当本国际标准省略任何明确的行为定义或程序使用错误的构造或错误的数据时,可能会出现未定义的行为。允许的未定义行为范围从完全忽略情况并产生不可预测的结果,在翻译或程序执行期间以环境特征的文件化方式(有或没有发布诊断消息),到终止翻译或执行(通过发布诊断消息)。许多错误的程序结构不会产生未定义的行为;他们需要被诊断。— 尾注 ]

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++ 标准定义的构造时,它可以做任何它想做的事情(可能会向我发送电子邮件,或者可能会向您发送电子邮件,或者可能完全忽略代码)。



C++ standard n3337 §1.3.25 unspecified behavior

C++ 标准 n3337 §1.3.25 未指定行为

behavior, for a well-formed program construct and correct data, that depends on the implementation [ Note: The implementation is not required to document which behavior occurs. The range of possible behaviors is usually delineated by this International Standard. — end note ]

对于格式良好的程序构造和正确的数据,行为取决于实现[注意:实现不需要记录发生了哪种行为。本国际标准通常描述了可能行为的范围。— 尾注 ]

C++ Standard doesn't impose particular behavior on some constructs but says instead that a particular, well defined behavior has to be chosen ( bot not necessary described) by particular implementation (version of library). So in the case when no description has been provided it can be difficult to the user to know exactly how will program behave.

C++ 标准没有对某些构造强加特定行为,而是说必须通过特定实现(库版本)选择特定的、定义良好的行为(不需要描述 bot)。因此,在没有提供描述的情况下,用户可能很难确切知道程序的行为方式。

回答by Suraj K Thomas

Implementation defined-

实现定义-

Implementors wish,should be well documented,standard gives choices but sure to compile

实施者希望,应该有详细记录,标准提供选择但一定要编译

Unspecified -

未指定 -

Same as implementation-defined but not documented

与实现定义相同但未记录

Undefined-

不明确的-

Anything might happen,take care of it.

任何事情都有可能发生,要小心。