某些未定义的行为是否比其他行为更加未定义?

时间:2017-02-22 10:39:54

标签: c++ undefined-behavior

这就是我的意思:i++ + i++是未定义的,因此是对数组的越界写入。

超出范围的数组写入的不确定性是可以理解的:它可能被利用来运行任意代码,这是未定义的,你可以获得。我们称之为运行时未定义的行为

然而,对于i++ + i++,故事似乎有所不同。我们假设编译器生成某事。它究竟是什么未定义的。非常不确定。事实上,它是如此不确定,我们曾经听过猫可能怀孕(虽然最近 - 从CppCon 2016,我认为 - 人们开始意识到未定义的行为不能

然而,一旦我们打开框并且看到编译器生成了什么,并且它不是可利用的代码(注入,数据争用等 - 例如,编译器选择抛出i++ + i++完全离开了,并不完全是将要执行的内容 - 是不是从上的那一点完美定义了?

换句话说,最后一种情况是我们可以称之为编译时未定义行为。用cat术语来说,它类似于Schrödinger's cat,其状态未知直到你打开框(参见生成的程序集),此时你会看到实际的现实执行。 (我想知道不确定的行为是否会使有毒的死猫怀孕。)

当然,未定义的行为是用于标准的法律术语。问题是关于"行为"这在现实中发生。

4 个答案:

答案 0 :(得分:5)

"未定义的行为"是一个适用于标准的术语。如果标准说行为是未定义的(或者没有直接或间接地定义它),那么标准的实施可能会采取其作者希望的任何行动 - 或者让事情走自己的路,并导致一堆垃圾。小猫如果是这样,命运就决定了。从标准的角度来看,这是一个二进制,定义/未定义,没有"更多"或者"较少"定义

现在,除了标准,你还有现实:

所以 - 如果标准未定义某个行为,那么智能,友好的编译器将生成错误或警告(或运行时错误和编译时警告)。它被允许这样做;毕竟什么都没有禁止这样的反应!

稍微不那么友好和聪明,但仍然相当友好和聪明的编译器将尝试创建一个造成最小伤害的代码。比如说,如果你没有初始化你的指针,它仍会将它初始化为最有意义的东西,无论是nullptr,还是指定类的唯一实例,指针的类型等等。这并不是很聪明,因为它会掩盖错误,如果您转换到不同的编译器,或者行为得到标准化以及除了编译器生成的其他内容,或者违背您的意图,这可能会让您感到困惑。不过,它是 方法,而不是非法的。一些像Javascript这样的高级语言倾向于采用这种方式,尝试很难理解错误的代码。

此外,如果反应肯定是有道理的,并且节省了一天的时间,在99.999%的案例中做了作者想要的事情 - 其他作者也可能会开始实施它(即使只是作为一个提示警告信息中的修复),它最终可能会进入标准。

最后但并非最不重要的是,编译器将应用适合于单独部分的语法规则,并生成某些,这可能是没有多大意义的事情。它不太可能导致小猫,并且你绝对不能保证结果是可重复的 - 但通常它是可重复的。说,打电话'删除'指向未使用' new'创建的内容。将总是导致分段错误。但那只是"只有"练习,不以任何方式保证。

您实际上不太可能遇到一些软件会导致绝对不受欢迎的绝对不相关的导致未定义的行为,所以不要&#39 ;但是,你的猫还没来,但标准并没有禁止这个。如果您的软件的作者有一种非常扭曲的幽默感,那么您可能会面临更具创造性的东西。就像Unix上的PAM系统一样,在遇到缺少passwd文件(所有用户帐户定义)时告诉你登录"你不存在。走开。"

答案 1 :(得分:2)

" undefined"的含义在C ++标准中是行为"例如在使用错误的程序结构或错误数据时可能出现的情况,本国际标准没有强制要求"。 (该引用直接来自ISO / IEC 14882 - 1998年的C ++标准)。

没有提供"更多"的概念。或者"较少"在这个定义中未定义。如果标准对所需行为强加了一个或多个要求,则行为不是未定义的。

没有要求并强加一个或多个要求是相互排斥的,而不是程度问题。

当然,实现可以做任何想做的事情,包括在呈现具有某种形式的未定义行为的代码时表现一致。但编译器的作用对标准没有影响。该标准是评估实现(也称为编译器,库等)正确性的基础,而不是反过来。

答案 2 :(得分:1)

如果某个操作调用Implementation-Defined行为,则表示

  1. 该标准遵循实施者对该行为应该产生什么行为的判断;它没有努力禁止实现以任意方式表现,但是期望针对特定目标平台和应用领域的质量实现将以对该平台和领域合理的方式运行。

  2. 实现必须指定并记录操作产生的行为,无论是否切实可行,并记录任何代码可能从中受益的行为。

  3. 相反,如果某个动作调用未定义的行为,则表示

    1. 该标准遵循实施者对该行为应该产生什么行为的判断;它没有努力禁止实现以任意方式表现,但是期望针对特定目标平台和应用领域的质量实现将以对该平台和领域合理的方式运行。

    2. 在实施者判断中,在定义行为时没有任何价值的情况下,实施无需指定和记录行动产生的行为。

      < / LI>

      有些编制者不会解释&#34;未定义的行为&#34;作为行使判断的邀请,而是作为不需要判断的指示。然而,当C89出版时,该术语被广泛理解为具有与实现定义行为不同的含义,仅在定义特定行为不切实际的情况下;我没有看到后来的标准有意改变这一点的迹象。

      许多实现都不会努力适合所有可能的目的。代码不能期望在不适合其目的的实现上正确运行,并且代码在不适合的实现上运行时失败的事实不以任何方式暗示代码有缺陷,除非它未能指定要求实施必须满足。虽然没有标准认可的实现和行为类别,但是在编译器编写者试图做出判断的情况下,常识会相当远。

答案 3 :(得分:-2)

“查看生成的集合,此时您将看到要执行的实际现实”。

不,这只是暂停问题的另一个方面。给定UB可以生成任何东西。您可能手头有一些代码,甚至无法告诉 它是否会停止,更不用说结果会是什么。

(当然,停止问题也存在于定义良好的C ++中,但你没有自我修改的C ++。)

现在,为什么这是相关的? Halting问题并没有说所有程序都是不可预测的,事实上很多程序都是如此。 Halting问题只是说有三类程序,那些肯定会停止的程序,那些肯定不会停止的程序,以及一个无法预测的程序。因此,您始终可以从程序集中确定程序行为的假设是不真实的。这破坏了断言UB仍然会产生具有可预测行为的二进制文件的逻辑。