1

我很确定这是 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. 

这发生在最后一个 else if 的第一个 MyMapIterator::operator->

地图是空的,我知道 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
4

9 回答 9

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 加倍会导致在测试后发出跳转指令。

于 2009-05-07T23:15:50.470 回答
2

我怀疑您在其他地方的代码中有缓冲区溢出。

此其他代码正在设法破坏已发布代码正在使用的内存。

在调试模式下,额外的内存填充将改变行为。

更改其他随机代码也会改变行为,因为它会改变事物的相对位置。

基本上,您需要查看大量代码或使用 BoundsChecker 之类的工具来查找错误。

专注于数组或任何原始指针数学。也可以使用已删除的指针。

如果它们写入不会导致崩溃的地方,这样的错误可能会隐藏很长时间。出于同样的原因,它们经常神秘地消失。

于 2009-05-07T17:09:50.197 回答
1

似乎您在未显示的代码中有其他一些条件导致地图填充了一些无效数据。

在调用之前尝试转储映射的内容find(),并在其余代码中查找任何可能的未初始化值。

于 2009-05-07T16:58:31.847 回答
1

显然任何东西都可能有错误,甚至是 VC 编译器。因此,如果这部分代码中存在错误,您可以将其包装在#pragma中以关闭优化。如果在那之后它仍然崩溃,那么您的代码中的其他东西正在破坏您的地图(然后您需要打开 DrWatson 以获取转储,以便您可以检查 windbg 中的崩溃以尝试找出它发生了什么)。

要关闭优化使用:

#pragma optimize("g", off)

要重新打开它,要么关闭,要么使用特殊情况

#pragma optimize("", on)

它将您的优化设置重置为项目默认值。

于 2009-05-07T16:59:53.557 回答
1

优化器真的搞砸了你的调试信息。很有可能这条线路实际上在附近的另一条线路上发生故障。即使调试器告诉您异常发生在运算符的后半部分&&,也可能不是。

要诊断真正的问题,您可能希望将最后一个 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" 都没有打印并且它仍然在那里崩溃,那么确实有问题。

于 2009-05-07T17:06:41.217 回答
1

我想我遇到了同样的问题。

似乎只发生在 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() );
}

对我来说感觉像是一个编译器错误。

于 2011-01-26T22:36:45.807 回答
0

我认为您做出的假设可能是错误的:在成功检查myIt != m_myMap.end() && myIt->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.
}
于 2009-05-07T17:27:30.933 回答
0

是的,我知道我可以用迭代器相等性检查来包围整个事情,这不是最好的代码。关键是,它应该仍然有效,如果失败了,其他任何东西都可以。

当然,按照这种逻辑,您别无选择……您所做的任何事情都是一种解决方法。

我认为我的第一种方法是将这些东西包装在一个经过验证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.
    }
}
于 2009-05-07T17:09:30.540 回答
0
if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_A)

你为什么要访问 userStatus 。和 foo 与 -> ?

于 2009-05-07T17:35:32.443 回答