使用 std::map/boost::unordered_map 帮助理解段错误

Posted

技术标签:

【中文标题】使用 std::map/boost::unordered_map 帮助理解段错误【英文标题】:Help understanding segfault with std::map/boost::unordered_map 【发布时间】:2010-07-08 02:40:50 【问题描述】:

我有一些代码使用模板静态类来处理资源(图像、字体、网格数据等)管理,允许客户端代码执行以下操作:

ResourceManager<Texture>::init("data/textures");
ResourceManager<Font>::init("data/fonts");
// later ...
boost::shared_ptr<const Texture> tex = ResourceManager<Texture>::getResource("wall.png");
boost::shared_ptr<const Font> font = ResourceManager<Font>::getResource("Arial.ttf");
// later ...
ResourceManager<Texture>::release();

“资源类型”必须有一个采用const std::string&amp; 的构造函数。

getResource实现如下:

static boost::shared_ptr<const ResourceType> getResource(const std::string& fileName)

    boost::shared_ptr<ResourceType> resource;

    typename table_t::const_iterator itr = _resources.find(fileName);
    if (itr == _resources.end()) 
        resource.reset(new ResourceType(_dataDirectory + fileName));
        _resources[fileName] = resource;
     else 
        resource = itr->second;
    

    return resource;

table_t 定义为typedef typename boost::unordered_map&lt; std::string, boost::shared_ptr&lt;ResourceType&gt; &gt; table_t;

_resources 的类型为 table_t

问题在于boost::unordered_map 我在调用find(来自find_iterator)时遇到了段错误。但是,使用std::map 代替,我在插入操作(源自_Rb_tree_decrement)或调用find(源自string::compare)时遇到段错误。

该问题仅在2次请求资源时出现(失败时文件名有效)。

由于mapunordered_map 都发生了这种情况,我假设我一定是在某个地方做了一些奇怪的事情来导致这种情况,有什么想法吗?

谢谢。

编辑:仍然有问题,我错了,它只发生在第二次请求资源时。但是,获取资源的前 2 次调用是成功的,这是导致段错误的第 3 次调用(每次调用都针对不同的资源)。

这是一个堆栈跟踪:

Program received signal SIGSEGV, Segmentation fault.
0x00000000004b4978 in boost::unordered_detail::hash_table<boost::unordered_detail::map<std::string, boost::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, boost::shared_ptr<Texture> > > > >::find_iterator (this=0x7aed80, bucket=0x38, k=...)
    at /usr/local/include/boost/unordered/detail/table.hpp:55
55          node_ptr it = bucket->next_;
(gdb) bt
#0  0x00000000004b4978 in boost::unordered_detail::hash_table<boost::unordered_detail::map<std::string, boost::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, boost::shared_ptr<Texture> > > > >::find_iterator (this=0x7aed80, bucket=0x38, k=...)
    at /usr/local/include/boost/unordered/detail/table.hpp:55
#1  0x00000000004b294c in boost::unordered_detail::hash_table<boost::unordered_detail::map<std::string, boost::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, boost::shared_ptr<Texture> > > > >::find (this=0x7aed80, k=...)
    at /usr/local/include/boost/unordered/detail/table.hpp:583
#2  0x00000000004b07c1 in boost::unordered_map<std::string, boost::shared_ptr<Texture>, boost::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, boost::shared_ptr<Texture> > > >::find (this=0x7aed80, k=...)
    at /usr/local/include/boost/unordered/unordered_map.hpp:423
#3  0x00000000004ae7c6 in ResourceManager<Texture>::getResource (fileName=...) at /home/tim/Projects/gameproj/app/ResourceManager.hpp:52
#4  0x00000000004ce7fc in Map::loadCellTextures (this=0x7fffffffdfc0, in=...) at /home/tim/Projects/gameproj/app/Map.cpp:57
#5  0x00000000004ce632 in Map (this=0x7fffffffdfc0, fileName=...) at /home/tim/Projects/gameproj/app/Map.cpp:30
#6  0x0000000000495702 in Game::init (xResolution=1024, yResolution=768) at /home/tim/Projects/gameproj/app/Game.cpp:116
#7  0x0000000000494fa0 in Game::run (xResolution=1024, yResolution=768) at /home/tim/Projects/gameproj/app/Game.cpp:38
#8  0x0000000000487f1d in Main::run (xResolution=1024, yResolution=768) at /home/tim/Projects/gameproj/app/Main.cpp:28
#9  0x0000000000487db5 in main (argc=1, argv=0x7fffffffe398) at /home/tim/Projects/gameproj/app/main.cpp:10

【问题讨论】:

_resources 是如何声明/初始化/销毁/等的? 在 sig11 发生时从 gdb 添加回溯可能有助于诊断。 _resources 被声明为static table_t _resources;,并在类定义下方使用:template &lt;typename ResourceType&gt; boost::unordered_map&lt; std::string, boost::shared_ptr&lt;ResourceType&gt; &gt; ResourceFactory&lt;ResourceType&gt;::_resources; 进行初始化。有一个静态的release 函数被调用来释放资源,这只是通过_resources.clear() 完成的。 你解决了这个问题吗? 我在 ubuntu 64 上与 qmake 有一个非常相似的问题。奇怪的是相同的代码在 blackberry 10 (qnx) 上运行得很好 【参考方案1】:

我没有发现任何明显的错误,您是否尝试过Valgrind(假设您运行某种 *nix 系统)?它是查找内存错误的宝贵工具,看起来它可能就是其中之一。

【讨论】:

+1 推荐 valgrind,它是一个非常宝贵的工具,值得您花时间学习它。 我对 Valgrind 有点熟悉,当发生段错误时,我会收到“大小为 8 的无效读取”。违规行是 table.hpp 中的node_ptr it = bucket-&gt;next_;(boost 的一部分)。使用调试器,bucket 似乎悬空/未初始化。 @Tim:这是一个线索,但我怀疑错误在于 boost 本身。如果您提供更多上下文,那么有人可能会发现它。【参考方案2】:

该问题仅在第二次请求资源时出现

这表明你的代码可能正在释放资源 - 第一次一切正常,然后你释放它,然后容器下次尝试访问该内存时,它会出现段错误。

【讨论】:

【参考方案3】:

看我遇到了类似的问题。 我的课程在 BlackBerry 的编译器和 qmake 4 上运行良好。但在 qtcreator 5(64 和 32b)上我遇到了同样的问题。 我追踪了这个 http://www.instructables.com/answers/Why-would-an-empty-stdmap-seg-fault-on-the-first/#CXBBGN4GQO92M2Q

看起来默认构造函数在某些编译器上没有正确初始化。 我在父类构造函数上添加了clear(),现在似乎可以工作了。

如果您有一些静态声明(或更糟糕的外部声明)并且应该实例化地图的时间不是预期的,也会发生这种情况。

希望对你有帮助。

【讨论】:

以上是关于使用 std::map/boost::unordered_map 帮助理解段错误的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)