如果在这些写入之后没有代码,为什么C ++编译器只会消除无用的写入?

时间:2011-09-01 12:46:07

标签: c++ visual-c++ optimization compiler-construction compiler-optimization

我正在检查Visual C ++ 10优化功能,并发现了一个相当奇怪的事情。此处的所有代码均使用/ O2编译。

在以下代码中:

int _tmain(int argc, _TCHAR* argv[])
{
    char buffer[1024] = {};
    MessageBoxA( 0, buffer, buffer, 0 );
    memset( buffer, 0, sizeof( buffer ) );
    return 0;
}

从机器代码中删除memset()之前return的调用(我检查反汇编)。这是完全合理的 - 如果之后没有来自buffer的读取,那么memset()是无用的,如果开发人员真的想要覆盖缓冲区,那么可以使用SecureZeroMemory()代替。

但是在以下代码中:

int _tmain(int argc, _TCHAR* argv[])
{
    char buffer[1024] = {};
    MessageBoxA( 0, buffer, buffer, 0 );
    memset( buffer, 0, sizeof( buffer ) );
    Sleep( 0 ); //<<<<<<<<<<<<<<<<<<<<<<<<<<< Extra code
    return 0;
}

memset()的调用未被删除。该调用对观察到的行为没有影响,可以像在第一个片段中一样被消除。

这可能是一个编译器缺陷,或者它可能在某种程度上有用 - 我无法决定。

为什么在为第二个代码段发出的机器代码中调用memset()会有用吗?

2 个答案:

答案 0 :(得分:18)

编译器可能无法判断MessageBoxA是否未创建buffer的别名,后来由Sleep使用。因此它没有通过“假设”检查。

答案 1 :(得分:2)

编译器可以查看memset的内容并确定它的作用。 Sleep()是一个与内核交互的系统调用,其行为取决于运行代码的Windows版本;包括尚未实现的Windows版本的可能性。编译器根本无法知道函数将执行什么操作,因此无法围绕它进行优化。

同样可以说MessageBox让我感到惊讶的是memset在第一个版本中被删除了。

可以肯定的是,对memset的调用在任何当前或未来版本的Windows上都不会出现问题,但这不是我希望编译器试图猜测的。