什么是C和C ++中的未定义行为? 未指定的行为和实现定义的行为呢? 它们之间有什么区别?
#1楼
摘自正式的C基本原理文档
术语未指定行为, 未定义行为和实现定义的行为用于对编写程序的结果进行分类,这些程序的属性标准不能或不能完全描述。 采用这种分类的目的是允许实现中的某种变化,这使得实现的质量成为市场上的积极力量,并允许某些流行的扩展,而不会消除对标准的崇高敬意。 该标准的附录F列出了属于这三种类别之一的那些行为。
未指定的行为使实施者可以自由翻译程序。 这种纬度不会扩展到无法翻译程序的程度。
未定义的行为使实施者可以不捕获某些难以诊断的程序错误。 它还确定了可能的符合语言扩展的领域:实现者可以通过提供正式未定义行为的定义来扩展语言。
实施定义的行为使实施者可以自由选择适当的方法,但要求向用户说明这一选择。 通常,指定为实现定义的行为是指用户可以根据实现定义做出有意义的编码决策的行为。 在确定实施定义应有多广泛时,实施者应牢记此标准。 与未指定的行为一样,仅不翻译包含实现定义的行为的源就不足为响应。
#2楼
C ++标准n3337§1.3.10 实现定义的行为
行为,对于格式正确的程序构造和正确数据,取决于实施以及每个实施文档
有时,C ++ Standard并未在某些构造上强加特定的行为,而是说必须通过特定的实现(库的版本)来选择和描述特定的,定义明确的行为。 因此,即使Standard并未对此进行描述,用户仍然可以确切知道程序的行为方式。
C ++标准n3337§1.3.24 未定义行为
本国际标准不施加任何要求的行为[注:当本国际标准省略行为的任何明确定义或程序使用错误的构造或错误的数据时,可能会出现未定义的行为。 允许的不确定行为的范围从完全忽略具有无法预测结果的情况到在翻译或程序执行过程中以环境特征的书面方式记录的行为(带有或不带有诊断消息)到终止翻译或执行(带有发布)诊断消息)。 许多错误的程序构造不会引起未定义的行为。 他们需要被诊断。 —尾注]
当程序遇到未根据C ++标准定义的构造时,它可以做任何想做的事情(也许给我发送电子邮件,或者给你发送电子邮件,或者完全忽略代码)。
C ++标准n3337§1.3.25 未指定的行为
行为,对于格式正确的程序构造和正确数据,取决于实现[注:不需要实现来记录发生哪种行为。 可能的行为范围通常由本国际标准来描述。 —尾注]
C ++ Standard并未在某些构造上强加特定的行为,而是说必须通过特定的实现(库的版本)选择特定的,定义明确的行为( 无需描述bot )。 因此,在没有提供任何描述的情况下,用户可能很难确切知道程序的行为方式。
#3楼
好吧,这基本上是标准的直接复制粘贴
3.4.1 1个实施定义的行为未指定的行为,其中每个实施记录了如何做出选择
2示例实现定义的行为的一个示例是有符号整数右移时的高位传播。
3.4.3 1使用国际标准不要求的非便携式或错误程序构造或错误数据时的不确定行为
2注释可能的不确定行为包括:完全忽略具有不可预测结果的情况,以环境特征的书面方式在转换或程序执行期间的行为(有或没有发出诊断消息),终止转换或执行(有诊断消息的发布)。
3示例未定义行为的一个示例是整数溢出时的行为。
3.4.4 1 未指明的行为使用未指明的值,或在本国际标准提供两种或两种以上可能性且在任何情况下均不对其施加任何其他要求的情况下的其他行为
2示例未指定行为的示例是对函数的参数进行评估的顺序。
#4楼
他们的最终总结:
综上所述,除非您的软件需要具有可移植性,否则通常不必担心未指定的行为。 相反,未定义的行为始终是不希望的,并且永远不会发生。
#5楼
也许措辞比标准的严格定义更容易理解。
实现定义的行为
这种语言说我们有数据类型。 编译器供应商指定应使用的大小,并提供有关其所做工作的文档。
未定义的行为
您做错了。 例如,您在不适合char的int中具有非常大的值。 您如何将这个值放入char ? 其实没有办法! 可能发生任何事情,但是最明智的做法是将int的第一个字节放在char 。 分配第一个字节是错误的,但这就是幕后的事情。
未指明的行为
这两个函数中的哪个先执行?
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?
语言没有指定评估,从左到右或从右到左! 因此,未指定的行为可能会导致未定义的行为,也可能不会导致未定义的行为,但是当然您的程序不应产生未指定的行为。
@eSKay,我认为您的问题值得编辑答案以进一步阐明:)
对于fun(fun1(), fun2()); 行为不是“实现定义的”吗? 毕竟,编译器必须选择一个或另一个过程?
实现定义的定义和未指定的定义之间的区别在于,编译器应该在第一种情况下选择一种行为,而在第二种情况下则不必选择行为。 例如,一个实现必须具有一个且只有一个sizeof(int)定义。 因此,不能说sizeof(int)对于程序的某些部分为4,对于其他部分为8。 与未指定的行为不同,在编译器可以说“确定”的情况下,我将从左到右评估这些参数,而从下到右评估下一个函数的参数。 它可以在同一程序中发生,因此被称为unspecified 。 实际上,如果指定了一些未指定的行为,C ++可能会变得更容易。 在这里看看Stroustrup博士的回答 :
据称,可以给编译器带来这种自由的结果与需要“从左至右进行常规评估”之间的差异可能会很大。 我不敢相信,但是有无数的编译器“在那儿”利用了自由,并且有些人热情地捍卫了这种自由,因此进行更改将很困难,并且可能需要数十年才能渗透到C和C ++世界的遥远角落。 令我失望的是,并非所有编译器都针对++ i + i ++之类的代码发出警告。 同样,参数的评估顺序也是不确定的。
IMO太多的“事物”是未定义的,未指定的,实现定义的等等。但是,这很容易说,甚至给出示例,但很难解决。 还应注意,避免大多数问题并生成可移植代码并不是很困难。