Visual Studio 中发布中令人困惑的 STL 错误
-
08-07-2019 - |
题
我非常确定 Visual Studio 2005 中的优化器存在错误。问题出在 STL 地图上。
这是相关代码:
MyMapIterator myIt = m_myMap.find(otherID);
if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_A)
{
//Prints stuff. No side-effects whatsoever.
}
else if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_B && myIt->second.foo == FOO_A)
{
//Prints stuff. No side-effects whatsoever.
}
else if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_B && myIt->second.foo == FOO_B && /*other meaningless conditions */)
{
//Prints stuff. No side-effects whatsoever.
}
在调试中完美运行,在发布中经常崩溃,禁用全局优化“修复了它”现在,没有任何效果。我得到一个:
Microsoft Visual Studio C Runtime Library has detected a fatal error in [...]
Press Break to debug the program or Continue to terminate the program.
这发生在 第一个 MyMapIterator::operator-> 最后一个 else if
地图是空的,我知道 find 应该返回 end(),前两次比较该效果有效。但不知何故,第三次 'myIt != m_myMap.end()' 返回 true,并且 && 的右侧被执行。
其他各种地方都会像这样失败,“myIt!= m_myMap.end()”的变体在同一文件中返回 true,但对我来说,这是排除大多数其他可能性的地方。我曾经认为这是一个缓冲区溢出,影响了我的地图,但回顾一下代码。我确信没有其他线程会破坏它,并且这是 100% 可重现的。
那么,我该怎么办呢?这对性能一点也不敏感。我只需要它正常工作。任何选项都是可以接受的。是的,我知道我可以用迭代器相等性检查来包围整个事情,但这不是最好的代码。关键是,它应该仍然有效,如果失败了,其他任何方法都可以。
编辑
最后一个 else-if 不会生成任何跳转!
if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_A)
009270BE mov ecx,dword ptr [this]
009270C4 add ecx,0Ch
009270C7 lea eax,[ebp-90h]
009270CD call std::_Tree<std::_Tmap_traits<unsigned __int64,lux::Foo,std::less<unsigned __int64>,std::allocator<std::pair<unsigned __int64 const ,lux::Foo> >,0> >::end (8A21E0h)
009270D2 mov esi,eax
009270D4 lea edi,[myIt]
009270D7 call std::_Tree<std::_Tmap_traits<unsigned __int64,lux::Foo,std::less<unsigned __int64>,std::allocator<std::pair<unsigned __int64 const ,lux::Foo> >,0> >::const_iterator::operator!= (8A2220h)
009270DC movzx ecx,al
009270DF test ecx,ecx
009270E1 je lux::Bar::DoStuff+0E4h (927154h)
009270E3 lea esi,[myIt]
009270E6 call std::_Tree<std::_Tmap_traits<unsigned __int64,lux::Foo,std::less<unsigned __int64>,std::allocator<std::pair<unsigned __int64 const ,lux::Foo> >,0> >::iterator::operator-> (8A21F0h)
009270EB cmp dword ptr [eax+8],1
009270EF jne lux::Bar::DoStuff+0E4h (927154h)
{
Stuff
}
else if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_B)
00927154 mov ecx,dword ptr [this]
0092715A add ecx,0Ch
0092715D lea eax,[ebp-98h]
00927163 call std::_Tree<std::_Tmap_traits<unsigned __int64,lux::Foo,std::less<unsigned __int64>,std::allocator<std::pair<unsigned __int64 const ,lux::Foo> >,0> >::end (8A21E0h)
00927168 mov esi,eax
0092716A lea edi,[myIt]
0092716D call std::_Tree<std::_Tmap_traits<unsigned __int64,lux::Foo,std::less<unsigned __int64>,std::allocator<std::pair<unsigned __int64 const ,lux::Foo> >,0> >::const_iterator::operator!= (8A2220h)
00927172 movzx edx,al
00927175 test edx,edx
00927177 je lux::Bar::DoStuff+17Ah (9271EAh)
00927179 lea esi,[myIt]
0092717C call std::_Tree<std::_Tmap_traits<unsigned __int64,lux::Foo,std::less<unsigned __int64>,std::allocator<std::pair<unsigned __int64 const ,lux::Foo> >,0> >::iterator::operator-> (8A21F0h)
00927181 cmp dword ptr [eax+8],2
00927185 jne lux::Bar::DoStuff+17Ah (9271EAh)
{
//Stuff
}
else if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_C)
009271EA mov ecx,dword ptr [this]
009271F0 add ecx,0Ch
009271F3 lea eax,[ebp-0A0h]
009271F9 call std::_Tree<std::_Tmap_traits<unsigned __int64,lux::Foo,std::less<unsigned __int64>,std::allocator<std::pair<unsigned __int64 const ,lux::Foo> >,0> >::end (8A21E0h)
009271FE mov esi,eax
00927200 lea edi,[myIt]
00927203 call std::_Tree<std::_Tmap_traits<unsigned __int64,lux::Foo,std::less<unsigned __int64>,std::allocator<std::pair<unsigned __int64 const ,lux::Foo> >,0> >::const_iterator::operator!= (8A2220h)
00927208 lea esi,[myIt]
0092720B call std::_Tree<std::_Tmap_traits<unsigned __int64,lux::Foo,std::less<unsigned __int64>,std::allocator<std::pair<unsigned __int64 const ,lux::Foo> >,0> >::iterator::operator-> (8A21F0h)
{
//Stuff in the condition and after
解决方案 2
好吧,伙计们。经过很多痛苦和泪水。为了至少暂时修复它,我必须将代码稍微修改为大多数人建议的形式,这就是第一种情况应该是的:
if (myIt != m_myMap.end())
{
if (myIt->second.userStatus == STATUS_A)
{
//Prints stuff. No side-effects whatsoever.
}
else if (myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_A)
{
//Prints stuff. No side-effects whatsoever.
}
else if (myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_B && /*other meaningless conditions */)
{
//Prints stuff. No side-effects whatsoever.
}
}
然而,该错误仍然存在。这个宝石修复了它:
if (myIt != m_myMap.end())
if (myIt != m_myMap.end())
{
if (myIt->second.userStatus == STATUS_A)
{
//Prints stuff. No side-effects whatsoever.
}
else if (myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_A)
{
//Prints stuff. No side-effects whatsoever.
}
else if (myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_B && /*other meaningless conditions */)
{
//Prints stuff. No side-effects whatsoever.
}
}
是的。加倍 if 会导致测试后发出跳转指令。
其他提示
我怀疑你在其他地方有代码,其中包含缓冲区。
这个其他代码正在破坏已发布代码正在使用的内存。
在调试模式下,额外的内存填充将改变行为。
更改其他随机代码也会改变行为,因为它会改变事物的相对位置。
基本上,您需要查看大量代码或使用BoundsChecker之类的工具来查找错误。
专注于数组或任何原始指针数学。也可以使用删除的指针。
如果它们写入不会导致崩溃的地方,这样的错误可能会隐藏很长时间。出于同样的原因,他们经常神秘地消失。
在您未显示的代码中,您可能还有一些其他条件导致地图填充了一些无效数据。
在调用 find()
之前尝试转储地图的内容,并在其余代码中查找任何可能未初始化的值。
关闭优化使用:
#pragma optimize("g", off)
将其重新打开,或者更改为开启或使用特殊情况
#pragma optimize("", on)
将您的优化设置重置为项目默认值。
优化器真的搞砸了你的调试信息。 实际的线路很可能在不同的附近线路上发生故障。即使调试器告诉您&amp;&amp;&amp;
运算符的后半部分发生异常,也可能不是。
要诊断真正的问题,您可能希望将最后一个if语句分解为:
else if (myIt != m_myMap.end())
{
printf("TEST 1\n");
if (myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_B && /*other meaningless conditions */)
{
//Prints stuff. No side-effects whatsoever.
}
}
printf("TEST 2\n");
如果输出“TEST 1”,则迭代器出现问题。如果您得到“TEST 2”的输出,然后崩溃,然后很明显错误发生在后续代码中。如果两者都不是“TEST 1”。也不是“TEST 2”。打印并且它仍然在那里崩溃,然后某些东西真的错误。
我想我遇到了同样的问题。
似乎只在x64版本中发生(完全优化)。我的STL映射被确认为空(size()== 0),但是这行代码没有检测到空映射而是进入for循环:
typedef std::map<std::string,std::string> KeyValueMap;
printf( "size: %d\n", ExtraHeaders.size() ); // prints "size: 0"
for( KeyValueMap::iterator it= ExtraHeaders.begin(); it != ExtraHeaders.end(); it++ )
{
// this line is executed
printf( "%s\t%s\n", (*it).first.c_str(), (*it).second.c_str() );
}
感觉像编译器错误。
是的我知道我可以用迭代器相等性检查来包围整个事情,它不是最好的代码。关键是,它应该仍然有效,如果失败,其他任何事情都可以。
当然,有了这个逻辑,你没有选择......你做的任何事都是一种解决方法。
我认为我的第一种方法是将这些内容包装在一个已经验证 myIt
有效的块中:
if (myIt != m_myMap.end())
{
if (myIt->second.userStatus == STATUS_A)
{
//Prints stuff. No side-effects whatsoever.
}
else if (myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_A)
{
//Prints stuff. No side-effects whatsoever.
}
else if (myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_B && /*other meaningless conditions */)
{
//Prints stuff. No side-effects whatsoever.
}
}
我认为你做的假设可能是错误的:在成功检查 myIt!= m_myMap.end()&amp;&amp; myIt-&gt; second.userStatus
在同一次迭代中。我敢打赌,在第一次检查这些值时,会立即发生崩溃。这是优化器,使你看起来已经清除了前两个 if
条件。
尝试将一个完全不同的代码块放入循环中,看看它是否崩溃(我的赌注是它会 - 我认为你不会在前面看到 cerr
的输出崩溃。)
// New code block
MyMapIterator yourIt = m_myMap.find(otherID);
if (yourIt != m_myMap.end() && yourIt->second.userStatus == STATUS_A ||
yourIt->second.userStatus == STATUS_B)
{
cerr << "Hey, I can print something! " << std::ios::hex << this << endl;
}
// Original code
MyMapIterator myIt = m_myMap.find(otherID);
if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_A)
{
//Prints stuff. No side-effects whatsoever.
}
else if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_A)
{
//Prints stuff. No side-effects whatsoever.
}
else if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_B && /*other meaningless conditions */)
{
//Prints stuff. No side-effects whatsoever.
}
if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_A)
为什么要访问userStatus?和foo - &gt; ?