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

Leo*_*aar 1 c++ undefined-behavior

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

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

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

但是,一旦我们打开盒子,看看编译器生成了什么,并且它不是可利用的代码(注入,数据竞争等等 - 例如,编译器选择i++ + i++完全抛弃),是不是它将完全被执行 -是不是从那一点上完美定义了

换句话说,最后一种情况是我们可以称之为编译时未定义的行为.用猫来说,它类似于薛定谔的猫,在你打开盒子之前它的状态是未知(参见生成的装配),此时你会看到要执行的实际现实.(我想知道未定义的行为是否会使有毒的死猫怀孕.)

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

SF.*_*SF. 5

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

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

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

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

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

最后但并非最不重要的是,编译器将应用适合于单独部分的语法规则,并产生一些东西,可能是没有多大意义的东西.它不太可能导致小猫,并且你绝对不能保证结果是可重复的 - 但通常它是可重复的.比如,在指向未使用"new"创建的内容上调用"delete"将导致分段错误.但这是"唯一"的做法,不能以任何方式保证.

你不可能真正遇到的软件,会去让开,造成绝对不需要完全不相关的结果不确定的行为,所以不绝育你的猫,只是还没有,但标准不会禁止这一点.如果您的软件的作者有一种非常扭曲的幽默感,那么您可能会面临更具创造性的东西.就像,Unix上的PAM系统,在遇到缺少passwd文件(所有用户帐户定义)时告诉你登录"你不存在.走开."