不稳定和双重混乱

时间:2010-10-02 13:16:40

标签: c++ optimization floating-point volatile

int x = 2;
volatile int y = 2;

const int z = x/y;

int main(){
    int x = 2 + 3;

    double d = 7 / 3;
}

我在这里有三个问题:

首先,在这种情况下,编译器可以在编译时计算'z'的值为1吗?

其次,我观察到编译器不生成用于添加2和3来初始化x的汇编指令。它用5直接初始化x。用'd'可以做同样的事情吗?

第三,有没有关于这两个概念的好书?标准中的任何引用都会有所帮助(标准文档似乎是一个有趣的地方,但非常可怕)

2 个答案:

答案 0 :(得分:4)

  

首先,在这种情况下,编译器可以在编译时计算'z'的值为1吗?

没有。读取或写入被认为具有副作用的volatile变量,因此不允许编译器执行此操作。

  

其次,我观察到编译器不生成用于添加2和3来初始化x的汇编指令。它用5直接初始化x。用'd'可以做同样的事情吗?

是。只要编译器可以证明没有副作用。例如。如果在计算过程中发生溢出或除零,则无法在编译时计算它,因为计算应该在运行时触发CPU异常。

  

第三,有没有关于这两个概念的好书?

是。 C ++ ISO标准准确描述了您的要求。书籍很好地学习基础知识或理论。编写重新标记标准中描述的所有技术细节的书籍是没有意义的。

答案 1 :(得分:1)

至于“第一次” - y必须在初始化z时访问,但我不认为必须使用该访问的结果来计算z if 实现以某种方式知道它必须是2.对于这个程序,(我认为)只有2种方式可以有任何其他值:

  1. 它被调试器修改或对程序的其他干扰。
  2. 加载程序将易失性全局变量放在某些内存区域中,这些区域的行为与硬件上的正常内存不同。 (在这种情况下,这实际上是非常奇怪的实现定义的行为,到目前为止,我认为对于编写的代码不合法​​,但是如果程序或程序之外的构建过程的某些部分可以,则它是相关的以某种方式控制对象最终的位置。)
  3. 这两个都是实现可以排除的事情 - 在第二种情况下,通过了解加载器的行为方式,首先通过限制你希望用调试器实现的目标(“编写volatile变量导致令人惊讶的行为“)。对于调试器的用户来说令人失望,但标准并没有限制调试器的工作方式,或者“实际”包含的内存,它只是限制了有效的C ++实现和程序的作用,以及C ++“看到”的内容。

    在实践中,您认为编译器不会将易失性对象视为优化。它是一个非const对象,你必须怀疑定义非const volatile对象的唯一原因是因为它会以编译器不期望的方式改变[*]。你希望它只是阅读y并进行划分,但我认为可以通过一个案例来优化是合法的。

    对于“second” - 在程序的情况下,编译器可以在“as-if”规则下使用预先计算的值初始化d,前提是它知道值除法产生的值。对于您在程序中的问题,它可以完全删除d

    “如果它知道什么值产生分支”取决于实现 - 如果它支持更改IEEE舍入模式或等效,如果它不知道应该生效什么模式,那么一般它不知道事先就是简单算术的结果。

    “as-if”规则涵盖了85%的编译器优化。它已在标准的1.9节中介绍,值得一看。我同意整个文件是相当令人生畏的,它使用的语言有时是难以理解的,但你必须从某个地方开始,所以从你目前感兴趣的任何东西开始; - )

    [*]具体来说,这不是C ++ 03标准中以任何方式解决的问题,一些编译器(Microsoft)在其线程语义定义中涉及volatile