在C和c++中,什么是未定义的行为(UB) ?未指定的行为和实现定义的行为呢?它们之间的区别是什么?
当前回答
来自官方的C基本原理文档
The terms unspecified behavior, undefined behavior, and implementation-defined behavior 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 behavior gives the implementor some latitude in translating programs. This latitude does not extend as far as failing to translate the program. Undefined behavior gives 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-defined behavior 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.
其他回答
实现定义的,
实现者希望,应该有良好的文档,标准给出了选择,但一定要编译
未指定的,
与实现定义的相同,但没有文档化
未定义的,
不管发生什么事,都要小心。
c++标准n3337§1.3.10 实现定义的行为
行为,对于结构良好的程序构造和正确的数据,即 这取决于实现和每个实现文档
有时候c++标准并没有把特定的行为强加给某些结构,而是说一个特定的、定义良好的行为必须由特定的实现(库的版本)选择和描述。所以用户仍然可以确切地知道程序的行为,即使标准没有描述这一点。
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 ]
当程序遇到不是根据c++标准定义的构造时,它可以做任何它想做的事情(可能给我发一封电子邮件,也可能给你发一封电子邮件,或者可能完全忽略代码)。
c++标准n3337§1.3.25 未指明的行为
行为,对于结构良好的程序构造和正确的数据,即 依赖于实现[注:实现不是 需要记录发生的行为。可能的范围 行为通常是由本国际标准来描述的。- - -结束 请注意)
c++标准没有将特定的行为强加于某些结构,而是说特定的、定义良好的行为必须由特定的实现(库的版本)选择(bot没有必要描述)。因此,在没有提供描述的情况下,用户很难确切地知道程序将如何运行。
来自官方的C基本原理文档
The terms unspecified behavior, undefined behavior, and implementation-defined behavior 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 behavior gives the implementor some latitude in translating programs. This latitude does not extend as far as failing to translate the program. Undefined behavior gives 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-defined behavior 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.
未定义行为是C和c++语言中让来自其他语言的程序员感到惊讶的方面之一(其他语言试图更好地隐藏它)。基本上,即使许多c++编译器不会报告程序中的任何错误,也有可能编写出行为不符合可预测方式的c++程序!
让我们来看一个经典的例子:
#include <iostream>
int main()
{
char* p = "hello!\n"; // yes I know, deprecated conversion
p[0] = 'y';
p[5] = 'w';
std::cout << p;
}
变量p指向字符串字面量“hello!”\n”,下面的两个赋值尝试修改该字符串字面量。这个程序是做什么的?根据c++标准第2.14.5节第11段,它调用未定义的行为:
尝试修改字符串文字的效果未定义。
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.
未定义行为的其他例子包括访问超出其边界的数组,对空指针进行解引用,在对象的生命周期结束后访问对象,或者编写像i++ + ++i这样据称很聪明的表达式。
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 ]
具体来说,第1.3.24节指出:
允许的未定义行为的范围从完全忽略带有不可预测结果的情况,到在翻译或程序执行过程中以具有环境特征的文档化方式行事(有或没有发出诊断消息),到终止翻译或执行(发出诊断消息)。
你能做些什么来避免遇到未定义的行为?基本上,你必须阅读优秀的c++书籍,作者都知道他们在谈论什么。避免网上教程。避免bullschildt。
未定义的行为是丑陋的——比如“好的、坏的和丑陋的”。
好:一个能够编译并正常工作的程序。
坏的:有错误的程序,这种错误编译器可以检测到并报错。
丑陋的:一个程序有一个错误,编译器不能检测和警告,这意味着程序编译,可能在某些时候正确工作,但也在某些时候奇怪地失败。这就是未定义行为。
一些程序语言和其他正式系统努力限制“不确定性的鸿沟”——也就是说,它们试图安排事情,使大多数或所有程序不是“好”就是“坏”,只有极少数程序是“丑”的。然而,这是C语言的一个特征,它的“不确定性的鸿沟”相当宽。