为什么 C++ 编译器只会在写入后没有代码的情况下消除无用的写入?

问题描述 投票:0回答:2

我正在检查 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()
调用会有用?

c++ optimization visual-c++ compiler-optimization
2个回答
18
投票

编译器可能无法判断

MessageBoxA
没有创建
buffer
的别名,然后由
Sleep
使用。因此它未通过“假设”检查。


2
投票

编译器可以查看

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

同样的情况也适用于

MessageBox
,这让我惊讶于
memset
在第一个版本中被删除了。

可以肯定的是,对

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

© www.soinside.com 2019 - 2024. All rights reserved.