【问题标题】:SIGFPE when accessing unordered_map访问 unordered_map 时的 SIGFPE
【发布时间】:2012-11-27 09:16:25
【问题描述】:

我有一个unordered_map<Block, int>,其中 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&lt;Block&gt;&amp;

【问题讨论】:

  • 与你的问题无关,但你不应该把函数/类/等。在std 命名空间中。
  • @Joachim 不知道 g 何时将自己偷运到那里 ;) 还添加了 gdb 回溯。关于std 命名空间:据我所知,如果我不想在每次用我的结构声明一个映射时都指定哈希函数,那就是要走的路——如果有更好的方法,我显然会全神贯注,但是因为我只是专门研究一个不应该那么糟糕的现有结构吗?
  • @juanchopanza 是的,我知道,问题似乎不是哈希函数本身,而是映射的内部变量den 显然不应该是 0 的事实。
  • @JoachimPileborg,你的第一条评论是什么意思?专门化 std::hash 必须 在 std:: 中完成,这是完全合法的。你的建议很好,但专业化是个例外。 (另一个常见的例子是专门化 std::less)

标签: c++ gcc clang libstdc++


【解决方案1】:

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

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

    { return __num % __den; }

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

您能否确认当它崩溃时m._M_bucket_count 为零?

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

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

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

【讨论】:

  • 如果bucket_count 为零,如何解决问题?就我而言,我检查并发现它为零。现在该怎么办?
  • 阅读最后一段。调试您的程序以了解损坏是如何发生的。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    我遇到了完全相同的问题。这是由于 memset 意外应用于容器数据造成的。

    【讨论】:

    • 在这种情况下,将 unordered_map 作为另一个结构的一部分并在原始类中有指向该结构的指针可能会很有用,即使用 pImpl 成语。
    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

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

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

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

      这里有两个教训:

      1. 除非您非常了解自己在做什么,否则请勿修改 gcc 构建系统。
      2. 有时错误可能出在编译器上(但话又说回来,错误最终出在编译器的编译者身上......)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多