14

我有一个unordered_map<Block, int>with Block 是一个简单的结构,定义如下:

struct Block {
    size_t start;
    size_t end;

    bool operator==(const Block& b) const {
        return start == b.start && end == b.end;
    }
};

namespace std {
template<>
struct hash<Block> {
    size_t operator()(const Block& b) const {
        return b.start;
    }
};
} 

尝试访问地图时,我确实在 gdb 中收到以下错误消息(g++ 4.7.1 和 clang++ 3.1 都相同):

Program received signal SIGFPE, Arithmetic exception.
0x0000000000401e0b in std::__detail::_Mod_range_hashing::operator() (this=0x7fffffffd8e0, __num=0, __den=0)
    at /usr/include/c++/4.7/bits/hashtable_policy.h:245
245     { return __num % __den; }

我的 libstdc++ 版本是 3.4.17(即来自 GCC 4.7 的版本)

相关回溯:

#0  0x0000000000401e0b in std::__detail::_Mod_range_hashing::operator() (this=0x7fffffffd8e0, __num=0, __den=0)
    at /usr/include/c++/4.7/bits/hashtable_policy.h:245
#1  0x0000000000407199 in std::__detail::_Hash_code_base<Block, std::pair<Block const, int>, std::_Select1st<std::pair<Block const, int> >, std::hash<Block>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, true>::_M_bucket_index (this=0x7fffffffd8e0, __c=0, __n=0) at /usr/include/c++/4.7/bits/hashtable_policy.h:787
#2  0x0000000000405230 in std::_Hashtable<Block, std::pair<Block const, int>, std::allocator<std::pair<Block const, int> >, std::_Select1st<std::pair<Block const, int> >, std::equal_to<Block>, std::hash<Block>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, true, false, true>::_M_bucket_index
    (this=0x7fffffffd8e0, __k=..., __c=0) at /usr/include/c++/4.7/bits/hashtable.h:466
#3  0x00000000004038de in std::__detail::_Map_base<Block, std::pair<Block const, int>, std::_Select1st<std::pair<Block const, int> >, true, std::_Hashtable<Block, std::pair<Block const, int>, std::allocator<std::pair<Block const, int> >, std::_Select1st<std::pair<Block const, int> >, std::equal_to<Block>, std::hash<Block>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, true, false, true> >::at (
    this=0x7fffffffd8e0, __k=...) at /usr/include/c++/4.7/bits/hashtable_policy.h:474
#4  0x0000000000403001 in SplicedAlignment::FindOptimalEndBlock() const::{lambda(Block const&)#1}::operator()(Block const&) const (__closure=0x7fffffffd990, block=...) at splicing.cpp:151
#5  0x00000000004040b3 in std::for_each<__gnu_cxx::__normal_iterator<Block const*, std::vector<Block, std::allocator<Block> > >, SplicedAlignment::FindOptimalEndBlock() const::{lambda(Block const&)#1}>(__gnu_cxx::__normal_iterator<Block const*, std::vector<Block, std::allocator<Block> > >, SplicedAlignment::FindOptimalEndBlock() const::{lambda(Block const&)#1}, SplicedAlignment::FindOptimalEndBlock() const::{lambda(Block const&)#1}) (__first=..., __last=..., __f=...)
    at /usr/include/c++/4.7/bits/stl_algo.h:4442

编辑:只要我给它相同的参数,我认为调用函数实际上不会有什么不同,但显然它确实如此:

std::for_each(blocks.begin(), blocks.end(), [&](const Block& block) {
   map.at(block);
}

导致错误,而只有:

const Block& block = blocks[0];
map.at(block);

工作得很好(blocks很简单vector<Block>&

4

5 回答 5

14

另外:如果你的哈希函数不能抛出,那么给它一个noexcept异常规范是非常重要的,否则哈希表需要将每个元素的哈希码与元素本身一起存储(这会增加内存使用并影响性能),以便容器操作必须不抛出不必重新计算哈希码。

SIGFPE 意味着除以零,并且从回溯中它发生在这里:

    { return __num % __den; }

这可能意味着__den为零。该值来自哈希映射的存储桶计数,不应为零。

你能确认它崩溃m._M_bucket_count的时间为零吗?

如果是这样,则表明您已经以某种方式损坏了地图(您是否尝试过编译-D_GLIBCXX_DEBUG以打开 libstdc++ 调试模式检查?您是否尝试过在 下运行valgrind?)或者 libstdc++ 代码中存在错误(这是可能的,但不太可能)。

下面的一些其他答案给出了地图如何被破坏的例子,例如为它分配存储空间malloc但实际上没有在该存储空间中构造一个对象,或者用 覆盖该对象memset

另一种可能性是您的哈希映射是一个全局变量,并且您正在从另一个全局变量的构造函数访问它,该变量运行到Static Initialization Order Fiasco。如果地图在其构造函数运行之前使用,则存储桶计数将为零。

于 2013-01-10T17:42:45.610 回答
8

在我的情况下,由于静态初始化失败而发生了同样的问题。从一个目标文件中,我为第二个目标文件中定义的静态 std::unordered_map 调用了 emplace() 。由于开始时数据位于 BSS,桶计数的值为零 => SIGFPE。

于 2016-04-04T15:27:30.330 回答
4

我有完全相同的问题。它是由 memset 意外应用于容器数据引起的。

于 2014-10-19T06:31:58.530 回答
1

还将在 STL 代码中发布我的 FPE 情况。

代码使用 malloc() 来分配 std::unordered_map。这当然不会调用构造函数,导致异常状态。

于 2020-03-31T02:30:14.730 回答
0

按照其他用户报告他们自己在 STL 代码中导致 FPE 的情况的传统,这里是我的:

自从我们升级编译器以来,我们的代码非常好,在 std::map 中开始因 SIGFPE 而死。结果,我在 c++11 模式下用 gcc 4.8.5编译了 gcc 9.2 ,这不是正常的事情——正常的 gcc 编译总是在 gnu++98 模式下执行。

这导致 4.8.5 gcc(对 c++11 的支持不完整/非官方)产生了错误的 9.2 gcc,偶尔会产生会出现各种不相关故障的二进制文件。例如,尝试调试上述 SIGFPE 会导致使用 编译时 std::basic_string 析构函数中出现 SIGSEGV -D_GLIBCXX_DEBUG,并且使用 检测到不相关的内存损坏-fsanitize=address

这里的教训有两个:

  1. 不要修改 gcc 构建系统,除非你非常清楚自己在做什么。
  2. 有时错误可能出在编译器中(但话又说回来,错误最终出现在编译器的任何人身上......)。
于 2019-12-20T10:34:30.213 回答