22

C++ 中不确定的行为和未定义的行为有什么区别?这种分类对 C 代码也有效吗?

4

3 回答 3

38

以下注释基于 C 标准ISO-9899而非 C++ 标准,但含义基本相同(参见 C 标准的第 3.4 和 4 节;另请参见 C++ 标准ISO-14882的第 1.3 节; 后一个文件没有这样定义“未指定的值”,但后来确实使用了该短语,具有明显的含义)。官方标准文件不是免费的(事实上,它们很贵),但上面的链接是委员会页面,包括标准的免费“草稿”,您可以认为它基本上等同于最终的标准文本。

这些术语描述了一个模糊的阶梯。

于是,往下走……

大多数时候,标准定义了在特定情况下应该发生的事情:如果你写c=a+bandabare int,那么c就是它们的总和(以一些细节为模)。当然,这是标准的重点。

实现定义的行为是标准列出了在特定情况下允许发生的两件或多件事情;它没有规定首选哪一个,但确实要求实现(解析 C 的实际编译器)在备选方案之间做出选择,始终如一地做同样的事情,并且实现必须记录它所做的选择。例如,单个文件是否可以被多个进程打开是实现定义的。

未指定的行为是标准列出了几个替代方案的地方,因此每个替代方案都符合标准,但没有进一步说明。一个实现必须在特定情况下选择一个备选方案,但不必每次都做同样的事情,也不必在文档中承诺它将做出何种选择。例如,a 中的填充位struct是未指定的。

未定义的行为是最极端的情况。在这里,所有的赌注都没有了。如果编译器或它生成的程序遇到未定义的行为,它可以做任何事情:它可以扰乱内存、破坏堆栈、HCF,或者在标准的极端情况下,让恶魔飞出你的鼻子。但大多数情况下它只会崩溃。所有这些行为都符合标准。例如,如果一个变量在同一个作用域中被声明,或者如果你写static int i;了,效果是未定义的。int i;#include <'my file'.h>

“价值”也有类似的定义。

指定的值是有效值,但标准没有说明它是什么。因此,标准可能会说给定函数返回一个未指定的值。您可以存储该值并在需要时查看它,而不会导致错误,但这并不意味着什么,并且该函数下次可能会返回不同的值,具体取决于月相。

实现定义的值就像实现定义的行为。像unspecified一样,它是一个有效值,但实现的文档必须自己提交将返回的内容,并且每次都做同样的事情。

一个不确定的值,甚至比 unspecified 还要unspecified。它要么是未指定的值,要么是陷阱表示。陷阱表示是某种神奇值的标准表述,如果您尝试将其分配给任何东西,则会导致未定义的行为。这不一定是实际值;考虑它的最佳方式可能是“如果 C 有异常,则陷阱表示将是异常”。例如,如果你int i;在一个块中声明,没有初始化,变量的初始值i不确定的,这意味着如果您在初始化之前尝试将其分配给其他东西,则行为是未定义的,编译器有权尝试上述恶魔般的技巧。当然,在大多数情况下,编译器会做一些不那么戏剧性/有趣的事情,比如将其初始化为 0 或其他一些随机有效值,但无论它做什么,你都无权反对。

所有这些不精确的重点是为编译器编写者提供最大的自由。这对编译器编写者来说很好(这也是让 C 编译器在如此广泛的平台上运行相当容易的原因之一),但它确实使事情变得更有趣,而不是让可怜的用户感到有趣。

编辑1:澄清不确定的值。

编辑 2:包含指向 C++ 标准的链接,并注意委员会草案基本上等同于最终标准,但免费。

于 2012-06-28T09:00:18.133 回答
8

我认为标准提到了未定义的行为和不确定的价值。所以一个是关于行为的,另一个是关于价值观的。

这两者有些正交,例如,在存在不确定值的情况下仍然可以很好地定义行为。

于 2012-06-28T08:18:24.753 回答
7

编辑 1: C11 和 C++11 的最新草案可在此处在线获得:C11 Draft N1570C++11 Draft n3242如果您没有最终标准的副本并且它们看起来很棒。(对文本外观的其他调整和一些措辞/语法编辑已经完成。)

编辑2:将所有出现的“行为”修复为“行为”以符合标准。

搜索 C++11 和 C11 标准,没有匹配到indeterminate ruleundefined rule。有诸如不确定值不确定序列不确定未初始化等术语。

如果在 Norman Gray 的回答中谈论陷阱和异常似乎很奇怪,请知道这些术语确实反映了C11 标准第 3 节中的相关定义。

C++ 依赖于 C 的定义。许多关于行为类型的有用定义可以在 C11 的第 3 节(在 C11 中)中找到。例如,不确定值在 3.19.2 中定义。请注意,C11 的第 2 节(规范性参考)提供了其他术语解释的其他来源,第 4 节定义了由于不符合标准而导致出现未定义行为等情况。

C11 的第 3.4 节定义了行为,3.4.1 定义了实现定义的行为,3.4.2 定义了特定于语言环境的行为,3.4.3 定义了未定义的行为,3.4.4 定义了未指定的行为。对于(第 3.19 节),有实现定义的值不确定的值未指定的值

松散地说,术语不确定是指一种未指定/未知的状态,它本身不会导致未定义的行为。例如,这个 C++ 代码涉及一个不确定的值:{ int x = x; }。(这实际上是 C++11 标准中的一个示例。)这里 x 首先被定义为整数,但此时它没有明确定义的值——然后它被初始化为任何值(不确定/未知)有它的价值!

众所周知的术语未定义行为在 C11 的 3.4.3 中定义,指的是

不可移植或错误的程序结构或错误数据,本国际标准对此没有要求

换句话说,未定义的行为是一些错误(在逻辑或状态中),接下来发生的任何事情都是未知的!因此可以制定一个未定义的 [行为] 规则,声明:在编写 C/C++ 代码时避免未定义的行为!:-)

一个不确定的 [行为] 规则是:避免编写不确定的代码,除非它是必要的,并且它不会影响程序的正确性或可移植性。因此,与未定义的行为不同,不确定的行为并不一定意味着代码/数据是错误的,但是,它的后续使用可能是错误的,也可能不是错误的——因此需要注意确保程序的正确性。

其他术语(如不确定顺序)在正文中(例如,C11 5.1.2.3 第 3 段;C++11,第 1.9 节第 13 段;即,在 [intro.execution] 中)。(您可能会猜到,它指的是未指定的操作步骤顺序。)

IMO 如果对所有这些细微差别感兴趣,则必须同时获得 C++11 和 C11 标准。这将允许人们探索定义等所需的所需详细程度。如果您没有此类链接,则此处提供的链接将帮助您使用最新发布的 C11 和 C++11 标准草案进行探索。

于 2012-07-06T20:09:17.520 回答