最佳答案是一个错误(但很常见)的误解:
未定义的行为是运行时属性*。它不能“时间旅行”!
某些操作(根据标准)定义为具有副作用,无法进行优化。执行I / O或访问volatile
变量的操作属于此类别。
但是,有一个警告:UB可以是任何行为,包括撤消先前操作的行为。在某些情况下,这可能会对优化早期代码产生类似的结果。
实际上,这与最佳答案中的引用(强调我的)一致:
执行格式正确的程序的合格实现应产生与具有相同程序和相同输入的抽象机相应实例的可能执行之一相同的可观察行为。
但是,如果任何这样的执行包含未定义的操作,则此国际标准对使用该输入执行该程序的实现没有任何要求(甚至不涉及第一个未定义的操作之前的操作)。
是的,此引号确实说“甚至不涉及第一个未定义操作之前的操作”,但请注意,这特别是与正在执行的代码有关,而不仅仅是编译。
毕竟,实际上未达到的未定义行为不会做任何事情,并且要实际到达包含UB的行,必须先执行它之前的代码!
因此,是的,一旦执行了UB,先前操作的任何效果都将变得不确定。但是在此之前,程序的执行是明确定义的。
但是请注意,可以将导致这种情况的程序的所有执行优化为等效程序,包括执行先前操作但撤消其效果的程序。因此,只要这样做,前面的代码就可能被优化掉,这等同于取消它们的效果。否则,不能。参见以下示例。
*注:这是不是不一致UB发生在编译时。如果编译器确实可以证明将始终对所有输入执行UB代码,则UB可以延长编译时间。但是,这需要知道所有先前的代码最终都会返回,这是一个很强的要求。同样,请参见下面的示例/说明。
要具体说明,请注意以下代码必须打印foo
并等待您的输入,而不管其后发生的任何未定义行为:
printf("foo")
getchar()
*(char*)1 = 1
但是,还请注意,不能保证foo
在UB发生后仍会保留在屏幕上,或者不能保证您键入的字符将不再位于输入缓冲区中。这两个操作都可以“撤消”,与UB“时间旅行”具有相似的效果。
如果getchar()
行是不存在,这将是合法的线路被优化掉,当且仅当那将是没有区别的输出foo
,然后“非做”吧。
两者是否难以区分将完全取决于实现(即,取决于编译器和标准库)。例如,您可以在等待另一个程序读取输出时在这里printf
阻塞线程吗?还是会立即返回?
当然,由于编译器知道其特定版本允许的行为printf
,因此可以进行相应的优化,因此printf
在某些情况下可能会优化,而在其他情况下则无法优化。但是,再次有理由证明,这与UB取消先前的操作是无法区分的,不是因为UB而使先前的代码“中毒了”。
a
未使用的问题(计算本身除外),然后删除a