C++ std::unordered_set SIGFPE异常

3
我写了一个程序,很有趣的是我运行了它数十次——我甚至记录了多次执行的结果——现在它不能工作了。
你可能认为我说的话很荒唐,或者我可能只是改变了一些代码,但我真的不记得对程序进行任何修改。
问题是一个SIGFPE,在程序的不同执行中被引发,根据输入而异。然而,在将值插入std::unordered_set*>时,会发生这种信号。
下面是我进行此类插入的代码片段:
std::vector<Point<T> *> _point, _centroid;
std::vector<std::unordered_set<Point<T> *> > _cluster;

// Main procedure -- pseudocode
for (point in _point) {
    cluster_id = centroid_with_min_distance(point, _centroid);
    has_changed = _change_cluster(point, cluster_id);
}

// Changes from one "point->_cluster's unordered_set" to "c's unordered_set"
bool _change_cluster(Point<T> *point, const unsigned int& c) {

    if ((point->_cluster == c) &&
        (_cluster[c].find(point) != _cluster[c].end())) {
        return false;
    }

    _cluster[point->_cluster].erase(point);
    _cluster[c].insert(point); // Insertion that raises the SIGFPE exception
    point->_cluster = c;
    return true;

}

这里是valgrind输出的一个重要部分:

==17636== Invalid read of size 8
==17636==    at 0x40A758: std::pair<std::__detail::_Hashtable_iterator<
                Point<unsigned int>*, true, false>, bool>
                std::_Hashtable<Point<unsigned int>*, Point<unsigned int>*,
                std::allocator<Point<unsigned int>*>,
                std::_Identity<Point<unsigned int>*>,
                std::equal_to<Point<unsigned int>*>,
                std::hash<Point<unsigned int>*>,
                std::__detail::_Mod_range_hashing,
                std::__detail::_Default_ranged_hash,
                std::__detail::_Prime_rehash_policy, false, true, true>::
                    _M_insert<Point<unsigned int>* const&>(
                        Point<unsigned int>* const&&&,
                        std::integral_constant<bool, true>) (hashtable.h:966)
==17636==    by 0x408EDA: std::_Hashtable<Point<unsigned int>*,
                 Point<unsigned int>*, std::allocator<Point<unsigned int>*>,
                 std::_Identity<Point<unsigned int>*>,
                 std::equal_to<Point<unsigned int>*>,
                 std::hash<Point<unsigned int>*>,
                 std::__detail::_Mod_range_hashing,
                 std::__detail::_Default_ranged_hash,
                 std::__detail::_Prime_rehash_policy, false, true, true>::
                     insert(Point<unsigned int>* const&) (hashtable.h:400)
==17636==  ... (calls from my program) ...
==17636==  Address 0x620a028 is not stack'd, malloc'd or (recently) free'd
==17636== 
==17636== 
==17636== Process terminating with default action of signal 8 (SIGFPE)
==17636==  Integer divide by zero at address 0x402D07D7C
==17636==    at 0x40252F: std::__detail::_Mod_range_hashing::operator()(
                 unsigned long, unsigned long) const (hashtable_policy.h:376)
==17636==    by 0x40A66C: std::__detail::_Hash_code_base<Point<unsigned int>*,
                 Point<unsigned int>*, std::_Identity<Point<unsigned int>*>,
                 std::equal_to<Point<unsigned int>*>,
                 std::hash<Point<unsigned int>*>,
                 std::__detail::_Mod_range_hashing,
                 std::__detail::_Default_ranged_hash, false>::_M_bucket_index(
                     Point<unsigned int>* const&, unsigned long,
                     unsigned long) const (hashtable_policy.h:758)
==17636==    by 0x40A772: std::pair<std::__detail::_Hashtable_iterator<
                 Point<unsigned int>*, true, false>, bool>
                 std::_Hashtable<Point<unsigned int>*, Point<unsigned int>*,
                 std::allocator<Point<unsigned int>*>,
                 std::_Identity<Point<unsigned int>*>,
                 std::equal_to<Point<unsigned int>*>,
                 std::hash<Point<unsigned int>*>,
                 std::__detail::_Mod_range_hashing,
                 std::__detail::_Default_ranged_hash,
                 std::__detail::_Prime_rehash_policy, false, true, true>::
                     _M_insert<Point<unsigned int>* const&>(
                     Point<unsigned int>* const&&&,
                     std::integral_constant<bool, true>) (hashtable.h:966)
==17636==    by 0x408EDA: std::_Hashtable<Point<unsigned int>*,
                 Point<unsigned int>*, std::allocator<Point<unsigned int>*>,
                 std::_Identity<Point<unsigned int>*>,
                 std::equal_to<Point<unsigned int>*>,
                 std::hash<Point<unsigned int>*>,
                 std::__detail::_Mod_range_hashing,
                 std::__detail::_Default_ranged_hash,
                 std::__detail::_Prime_rehash_policy, false, true, true>::
                     insert(Point<unsigned int>* const&) (hashtable.h:400)
==17636==    ... (calls from my program) ...

这里的问题是:我的程序中有一些计算可能会导致除以零,虽然它们与此进程没有直接关系,但是这个错误是否可能被插入操作掩盖?或者在向std::unordered_set<T>插入指针时,我需要进行额外处理吗?
我正在x86_64 GNU/Linux下编译程序,并使用g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3

你能否发布一段代码片段,围绕着出现问题的函数?你还执行了哪些集合操作?此外,似乎你正在使用std::hash<Point<T>*>std::equal_to<Point<T>*>作为哈希和相等函数,这意味着它是基于对象地址而不是点本身的数据进行哈希处理。 - Dave S
2
@Rubens:如果你的程序存在未定义行为,它可能会在运行了数十亿次后才崩溃。当你使用指针时,很容易出现未定义行为。另一个原因可能是你一直在修改unordered_set<>中的值。我相信如果你希望我们提供帮助,你需要向我们展示代码。 - Andy Prowl
@AndyProwl 谢谢,我已经尝试缩小代码范围,以便直接指向重点。请查看更新! - Rubens
@Rubens:我这里没有看到unordered_set<>,只是将unordered_map<>用作unordered_set<>。这是笔误吗? - Andy Prowl
1
@Rubens:你如何检查c是否小于_cluster的大小?我看到这里可能会越界。 - Andy Prowl
显示剩余2条评论
1个回答

5
我怀疑 'c' 超出了 _cluster 的范围。请将所有的 _cluster[c] 替换为 _cluster.at(c),看是否会出现超出范围的错误。
valgrind 输出表明这是一个无效读取后紧随整数模数错误。由于它执行的是 hash_code % bucket_count,我怀疑您已经访问了向量的范围之外。
另外,以 _ 开头命名事物可能存在风险,因为如果它在全局命名空间中,那么它在技术上是保留的。

谢谢。我喜欢用初始的“_”来命名私有成员,因为我通常不使用“this->”——这些实例在一个更大的类中,并且它们是私有的。我现在正在尝试这个解决方案! - Rubens
没错,这就是问题所在。天啊,我真的很羞愧,竟然没想到这个 -- 我猜可能是被 SIGFPE 误导了。非常感谢大家的支持! - Rubens

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接