使用自定义比较器 std::list::sort 上的分段错误?

Segmentation Fault on std::list::sort with custom comparator?

在某些代码中,我有一个包含 PlayerObject 类型对象的链表

std::list<PlayerObject> unknownPlayers;

而且我需要根据一些属性对对象进行排序,所以我使用class

class CountCmp
    : public std::binary_function< PlayerObject, PlayerObject, bool >
{
public:
    result_type operator()( const first_argument_type & lhs,
                            const second_argument_type & rhs ) const
      {
         return lhs.posCount() < rhs.posCount();
      }
};

unknownPlayers.sort( PlayerObject::CountCmp() );

但由于某种原因程序正在接收 SEGV。我使用 -fsanitize=address 编译标志进行调查,堆栈跟踪如下:

ASAN:SIGSEGV
=================================================================
==8521==ERROR: AddressSanitizer: SEGV on unknown address 0x0003000003e8 (pc 0x7fe7ca6d6540 bp 0x7fff07c51720 sp 0x7fff07c516e8 T0)
#0 0x7fe7ca6d653f in std::__detail::_List_node_base::_M_transfer(std::__detail::_List_node_base*, std::__detail::_List_node_base*) (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0x9e53f)
#1 0x881e58 in std::__cxx11::list<rcsc::PlayerObject, std::allocator<rcsc::PlayerObject> >::_M_transfer(std::_List_iterator<rcsc::PlayerObject>, std::_List_iterator<rcsc::PlayerObject>, std::_List_iterator<rcsc::PlayerObject>) /usr/include/c++/5/bits/stl_list.h:1747
#2 0x87f79f in std::__cxx11::list<rcsc::PlayerObject, std::allocator<rcsc::PlayerObject> >::splice(std::_List_const_iterator<rcsc::PlayerObject>, std::__cxx11::list<rcsc::PlayerObject, std::allocator<rcsc::PlayerObject> >&&, std::_List_const_iterator<rcsc::PlayerObject>) /usr/include/c++/5/bits/stl_list.h:1444
#3 0x87d22e in std::__cxx11::list<rcsc::PlayerObject, std::allocator<rcsc::PlayerObject> >::splice(std::_List_const_iterator<rcsc::PlayerObject>, std::__cxx11::list<rcsc::PlayerObject, std::allocator<rcsc::PlayerObject> >&, std::_List_const_iterator<rcsc::PlayerObject>) /usr/include/c++/5/bits/stl_list.h:1464
#4 0x87d90c in void std::__cxx11::list<rcsc::PlayerObject, std::allocator<rcsc::PlayerObject> >::sort<rcsc::PlayerObject::CountCmp>(rcsc::PlayerObject::CountCmp) (/home/felipe_coimbra/.../sample_player+0x87d90c)
#5 0x86c3e3 in rcsc::WorldModel::localizePlayers(rcsc::VisualSensor const&) /home/felipe_coimbra/.../world_model.cpp:2412
#6 0x864b37 in rcsc::WorldModel::updateAfterSee(rcsc::VisualSensor const&, rcsc::BodySensor const&, rcsc::ActionEffector const&, rcsc::GameTime const&) /home/felipe_coimbra/.../world_model.cpp:910
#7 0x81a7ac in rcsc::PlayerAgent::Impl::analyzeSee(char const*) /home/felipe_coimbra/.../player_agent.cpp:1552
#8 0x819b52 in rcsc::PlayerAgent::parse(char const*) /home/felipe_coimbra/.../player_agent.cpp:1393
#9 0x816519 in rcsc::PlayerAgent::handleMessage() /home/felipe_coimbra/.../player_agent.cpp:886
#10 0x75b3a8 in rcsc::BasicClient::runOnline(rcsc::SoccerAgent*) /home/felipe_coimbra/.../basic_client.cpp:158
#11 0x75ac9e in rcsc::BasicClient::run(rcsc::SoccerAgent*) /home/felipe_coimbra/.../basic_client.cpp:93
#12 0x5e933e in main /home/felipe_coimbra/.../main_player.cpp:102
#13 0x7fe7c9d6f82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#14 0x5e8ed8 in _start (/home/felipe_coimbra/.../sample_player+0x5e8ed8)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV ??:0      std::__detail::_List_node_base::_M_transfer(std::__detail::_List_node_base*, std::__detail::_List_node_base*)
==8521==ABORTING

在我看来,比较器可以用于严格弱排序。那么 std::list::sort 中出现分段错误的其他可能原因是什么?

编辑 1:

我尝试在排序之前使用 list::iteratorlist::reverse_iterator 遍历 unknownPlayers,它工作正常。我什至可以通过 posCount() 方法访问值,并且返回值没有任何奇怪之处。

编辑 2:

我一直在尝试为 post 选择相关代码,但我发现它有点麻烦。 我认为关于 unknownPlayers 的一些相关事实是:

  1. std::list::splice方法填充:

    unknownPlayers.splice(unknownPlayers.end(), new_unknown_players );

new_unknown_players也由splice填充。基本上在每个游戏周期中,都会有关于所见玩家的新信息,这些玩家可能被识别为队友、对手或根本没有被识别(未知玩家)。尝试通过接近阈值将未识别的玩家与先前识别的玩家(队友或对手)进行匹配,如果失败,则将它们拼接到 new_unknown_players 链表中。

  1. sort 如果我在上面的代码行中调用 splice 之前尝试对 unknownPlayers 进行排序,sort 也会以同样的方式崩溃

  2. 然而,当 unknownPlayers 为空时 sort 不会崩溃(谢天谢地,它并没有那么混乱)并且在第一次 splice 实际上添加新对象并首次使其不为空。

也许这意味着在代码的其他部分某些东西正在破坏列表?还是不明白为什么可以穿越

这部分代码:

//////////////////////////////////////////////////////////////////
// splice temporary seen players to memory list
// temporary lists are cleared
M_teammates.splice(M_teammates.end(),
                       new_teammates);
M_opponents.splice(M_opponents.end(),
                       new_opponents);
// I've put some debug in this line
M_unknown_players.splice(M_unknown_players.end(),
                             new_unknown_players);
// And here too

/////////////////////////////////////////////////////////////////
    // create team member pointer vector for sort

    PlayerPtrCont all_teammates_ptr;
    PlayerPtrCont all_opponents_ptr;

    {
        const PlayerCont::iterator end = M_teammates.end();
        for (PlayerCont::iterator it = M_teammates.begin();
             it != end;
             ++it) {
            all_teammates_ptr.push_back(&(*it));
        }
    }
    {
        const PlayerCont::iterator end = M_opponents.end();
        for (PlayerCont::iterator it = M_opponents.begin();
             it != end;
             ++it) {
            all_opponents_ptr.push_back(&(*it));
        }
    }

/////////////////////////////////////////////////////////////////
    // sort by accuracy count
    std::sort(all_teammates_ptr.begin(),
              all_teammates_ptr.end(),
              PlayerObject::PtrCountCmp());
    std::sort(all_opponents_ptr.begin(),
              all_opponents_ptr.end(),
              PlayerObject::PtrCountCmp());

// I've put some more debug here
M_unknown_players.sort( PlayerObject::CountCmp() );
// And here

好吧,问题是我最终自己解决了这个问题。

显然这是大量错误的附带影响。当我说巨大时,我的意思是几十个。未定义的行为真是令人头疼。

遗憾的是,像 Valgrind 这样的消毒程序和软件有时无法正确识别哪些代码正在破坏堆栈内存。在这种情况下,排序函数只是访问一个已经损坏的链表。想知道是否还有更多 precise/detailed 工具。

总之,谢谢大家