什么是C ++中的不确定行为? 它与未定义的行为有什么不同?

C ++中不确定行为和未定义行为之间有什么区别? 此分类是否也适用于C代码?

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

编辑2:修正所有出现的“行为”为“行为”以匹配标准。

搜索C ++ 11和C11标准时, 不确定规则未定义规则没有匹配项。 存在诸如不确定值不确定序列不确定未初始化等术语。

如果在Norman Gray的回答中谈论陷阱和exception似乎很奇怪,请知道这些术语确实反映了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中定义,并且指的是a的任何情况

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

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

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

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

IMO如果对所有这些细微差别感兴趣,那么获得C ++ 11和C11标准是必须的。 这将允许人们通过定义等探索所需的详细程度。如果您没有这样的链接,将帮助您使用上次发布的C11和C ++ 11标准草案进行探索。

以下评论基于C标准ISO-9899 ,而不是C ++标准,但其含义基本相同(参见C标准的3.4和4节;另见C ++标准, ISO-14882 ,第1.3节) ;后一个文档没有定义“未指定的值”,但后来使用该词组具有明显的含义)。 官方标准文件不是免费的(实际上,它们很昂贵),但上面的链接是委员会页面,并包括标准的免费“草稿”,您可以采取这些标准基本上等同于最终的标准文本。

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

所以,向下走……

大多数情况下,标准定义了在特定情况下应该发生的事情:如果你写c=a+babint ,则c是它们的总和(以某些细节为模)。 当然,这是标准的要点。

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

未指明的行为是标准列出几个备选方案的地方,每个备选方案都符合标准,但不再进一步。 实现必须选择其中一个替代方案来选择特定情况,但不必每次都做同样的事情,并且不必在文档中提交它将做出哪些选择。 例如, struct中的填充位未指定。

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

“价值”有类似的定义。

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

实现定义的值类似于实现定义的行为。 像未指定的那样 ,它是一个有效的值,但是实现的文档必须承诺将返回的内容,并且每次都做同样的事情。

不确定的值未指定的更加未指定 。 它可以是未指定的值或陷阱表示 。 陷阱表示是标准 – 代表一些神奇的价值,如果你试图将它分配给任何东西,会导致不确定的行为。 这不一定是实际价值; 可能最好的思考方式是“如果C有exception,陷阱表示将是一个例外”。 例如,如果您声明int i; 在一个块中,没有初始化,变量i的初始值是不确定的 ,这意味着如果在初始化之前尝试将其分配给其他东西,则行为是未定义的,并且编译器有权尝试所述的恶魔鼻子戏法。 当然,在大多数情况下,编译器会做一些不太戏剧性/有趣的事情,比如将其初始化为0或其他一些随机有效值,但无论它做什么,你都无权反对。

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

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

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

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

例如,这两者在某种程度上是正交的,在存在不确定值的情况下仍然可以很好地定义行为。