为什么 C++如果写入后没有代码,编译器只会消除无用的写入?
我正在检查 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;
}
从机器代码中消除了在 return
之前对 memset()
的调用(我检查了反汇编)。这是完全合理的 - 如果之后没有从 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()
调用会有用?
I'm inspecting Visual C++ 10 optimization capabilities and found a rather curious thing. All code herein is compiled with /O2.
In the following code:
int _tmain(int argc, _TCHAR* argv[])
{
char buffer[1024] = {};
MessageBoxA( 0, buffer, buffer, 0 );
memset( buffer, 0, sizeof( buffer ) );
return 0;
}
the call to memset()
before return
is eliminated from the machine code (I inspect the disassembly). This is perfectly reasonable - if there're no reads from buffer
afterwards then memset()
is useless and if the developers really want to overwrite the buffer thay can use SecureZeroMemory()
instead.
However in the following code:
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;
}
the call to memset()
is not eliminated. That call has no influence on observed behavior and could be eliminated just as well as in the first snippet.
This can be a compiler deficiency or it can be useful somehow - I can't decide.
Why could leaving memset()
call in the machine code emitted for the second snippet be useful?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
![扫码二维码加入Web技术交流群](/public/img/jiaqun_03.jpg)
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
编译器可能无法判断
MessageBoxA
没有创建buffer
的别名,稍后供Sleep
使用。因此它无法通过“假设”检查。The compiler probably can't tell that
MessageBoxA
doesn't create an alias tobuffer
that's then used bySleep
later. Thus it fails the "as-if" check.编译器可以查看memset的内容并确定它的作用。
Sleep()
是一个与内核交互的系统调用,其行为取决于代码运行的 Windows 版本;包括尚未实施的 Windows 版本的可能性。编译器根本无法知道该函数将做什么,因此它无法围绕它进行优化。MessageBox
也是如此,这让我惊讶于memset
在第一个版本中被删除了。可以肯定的是,对
memset
的调用在任何当前或未来版本的 Windows 上都不会出现问题,但这不是我希望编译器尝试猜测的事情。The compiler can look at the contents of
memset
and determine what it does.Sleep()
is a system call that interacts with the kernel and its behavior is dependent on what version of Windows the code is run on; including the possibility of yet to implemented versions of Windows. There is simply no way for the compiler to know what the function will do and therefore there is no way for it optimize around it.The same can be said of
MessageBox
which makes me surprised thatmemset
got removed in the first version.It is a safe bet that the call to
memset
will not be a problem on any current or future version of Windows, but that's not something I want the compiler to try to guess about.