所以,有些人可能会同意(代码 1):
void func()
{
int a, b;
{
//Do something with a and b
}
}
看起来比(代码 2)更好(或者是更好的做法):
void func()
{
int a;
{
int b;
//Do something with a and b
}
}
此外,比这个(代码 3):
void func()
{
{
int a, b;
//Do something with a and b
}
}
如果没有 a
或 b
的重新声明,这些代码是否等效?编译器会优化这个吗?
答案 0 :(得分:2)
对于平凡类型(这具有技术意义),它们的销毁/创建仅指示编译器在哪里保证不需要知道它们的值是什么。优化时可以“忽略”变量消失后任何可能的悬空引用或指针。
因此,在将使用的最小范围内创建数据有时会导致编译器生成更优化的代码。如果“某物”是一个编译器无法查看其细节的函数,该函数通过引用(或指针)获取整数,则编译器不知道需要在实际内存中保留该实际整数多长时间。但是(由程序员)保证在作用域结束后,它不需要实际内存中的实际整数。
除非出现这种情况,否则没有区别。当您要求时,编译器实际上不必创建 int
类型的对象;但有时他们会因其他要求而被迫这样做。
在调试版本中,编译器会在您要求的地方创建变量,并在您要求时清理它们,因为这样可以更轻松地单步执行代码和检查程序状态。
对于非平凡类型(基本上,具有 vtable 的对象,或具有构造函数的对象,或具有析构函数的对象,或在递归定义中包含非平凡类型的对象),对象的创建/销毁可能具有可见的副作用,因此什么时候发生很重要。编译器仍然可以在 as-if 规则下移动它们(即,程序集只需要像你写的一样行事),所以如果它可以证明那些非平凡的构造/销毁操作不关心什么时候确切地说,它们运行(在标准形式意义上)它们可以被编译器移动。
答案 1 :(得分:1)
在您的示例代码中,因为您使用了简单类型,例如 int
。但总的来说,如果您将使用对象,这可能是错误的。
考虑以下示例:
void func()
{
SomeObj a, b;
SomeOtherObj c;
{
//Do something with a and b
}
}
在这种情况下,对象 a
和 b
不能在 c
之前删除,因为对象总是以与创建相反的顺序删除。虽然理论上,如果没有可观察到的副作用,一些编译器可能会在 a
之后重新排列 b
和 c
的创建,并在到达函数结束之前将其删除。
第二种情况
void func()
{
SomeObj a;
SomeOtherObj c;
{
SomeObj b;
//Do something with a and b
}
}
b
将在作用域关闭后立即删除,但 a
的行为与第一种情况相同。
对于第三种情况,a
的行为与第二个中的 b
相同。