不稳定和双重混乱
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' 也可以做同样的事情吗?
第三,关于这两个概念有什么好书可以读吗?标准中的任何引用都会有所帮助(标准文档似乎是一个有趣的地方,尽管非常可怕)
int x = 2;
volatile int y = 2;
const int z = x/y;
int main(){
int x = 2 + 3;
double d = 7 / 3;
}
I have three questions here:
Firstly, can the compiler calculate the value of the 'z' at compile time to be 1 in this case?
Secondly, I observed that the compiler does not generate assembly instructions for adding 2 and 3 to initialize x. It directly initializes x with 5. Can the same be done with 'd'?
Thirdly, Is there any good book to read on these two concepts? Any quotes from the Standard would be helpful (The standard document seems to be an interesting place though very scary)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
至于“first” - 当
z
初始化时,必须访问y
,但我不认为该访问的结果必须用于计算z
,如果实现以某种方式知道它必须是 2。对于这个程序,(我认为)只有 2 种方法可以使其具有任何其他值:这两件事都是实现可以排除的 - 在第二种情况下,通过了解加载器的行为方式,首先通过对调试器希望实现的目标进行限制(“写入易失性变量会导致令人惊讶的行为” )。对于调试器的用户来说令人失望,但该标准并不限制调试器如何工作,或者内存“实际”包含什么,它只是限制有效的 C++ 实现和程序做什么,以及 C++“看到”什么。
在实践中,您可能会认为编译器不会费心将易失性对象视为需要优化的对象。它是一个非常量对象,您必须怀疑定义非常量易失性对象的唯一原因是因为它将以编译器不期望的方式发生变化[*]。您希望它只读取 y 并进行除法,但我认为可以为合法的优化提供一个案例。
至于“第二” - 在您的程序中,编译器可以根据“as-if”规则使用预先计算的值初始化
d
,前提是它知道除法产生什么值。就此而言,在您的程序中,它可以完全删除d
。“只要它知道除法产生什么值”取决于实现 - 如果它支持对 IEEE 舍入模式或等效模式的更改,并且如果它不知道应该采用哪种模式,那么通常它不会提前知道即使是简单算术的结果。
“as-if”规则涵盖了 85% 的编译器优化。标准第 1.9 节对此进行了介绍,值得一看。我同意整个文档相当令人生畏,而且它使用的语言有时令人费解,但你必须从某个地方开始,所以从你当前感兴趣的任何内容开始;-)
[*] 具体来说,这不是无论以何种方式在 C++03 标准中解决了这一问题,某些编译器 (Microsoft) 在其线程语义定义中都涉及
易失性
。As for "first" -
y
must be accessed, whenz
is initialized, but I don't think the result of that access has to be used to calculatez
, if the implementation somehow knows that it must be 2. For this program there are (I think) only 2 ways it can have any other value:Both of those are things that the implementation can rule out - in the second case by knowing how the loader behaves, in the first place by placing limitations on what you can hope to achieve with a debugger ("writing volatile variables results in surprising behavior"). Disappointing for the user of the debugger, but the standard doesn't constrain how debuggers work, or what memory "actually" contains, it just constrains what valid C++ implementations and programs do, and what C++ "sees".
In practice you'd think the compiler wouldn't bother treating a volatile object as subject to optimisations. It's a non-const object, and you've got to suspect that the only reason for defining a non-const volatile object is because it's going to change in ways the compiler doesn't expect[*]. You'd hope it will just read
y
and do the division, but I reckon a case could be made for optimisation being legal.As for "second" - in the case of your program, the compiler can initialize
d
with a pre-computed value under the "as-if" rule, provided it knows what value division produces. For that matter in your program it can removed
entirely."Provided it knows what value division produces" depends on the implementation - if it supports changes to IEEE rounding modes or equivalent, and if it doesn't know what mode should be in force, then in general it doesn't know in advance the result of even simple arithmetic.
The "as-if" rule covers, say, 85% of compiler optimisations. It's covered in section 1.9 of the standard, which is worth a look. I agree that the document as a whole is pretty intimidating, and the language it uses is sometimes impenetrable, but you have to start somewhere, so start with whatever you're currently interested in ;-)
[*] Specifically, and this is not something that's addressed in the C++03 standard in any way, some compilers (Microsoft) involve
volatile
in their definition of threading semantics.不可以。读取或写入易失性变量被认为有副作用,因此编译器不允许这样做。
是的。只要编译器能够证明没有副作用。例如,如果在计算过程中发生溢出或被零除,则无法在编译时进行计算,因为计算应在运行时触发CPU异常。
是的。 C++ ISO 标准准确地描述了您所要求的内容。书籍非常适合学习基础知识或理论。编写重新表述标准中描述的所有技术细节的书籍是没有意义的。
No. reading or writing to volatile variables considered having side effects, so the compiler is not allowed to do this.
Yes. As long as the compiler can prove that there are no side effect. E.g. if overflow or a divide by zero happens during the computation, it can't compute it at compile time since the computation should trigger a CPU exception at runtime.
Yes. C++ ISO standard describes exactly what you're asking. Books are good to learn the basics or the theory. It makes no sense writing books that rephrase all the technical details described in the standard.