在编写使用std::unordered_map容器的代码时,我遇到了在特定情况下报告SIGSEGV的问题。我将使用代码解释细节。下面是使用上述容器的类:
constexpr std::uint8_t MAX_COMPONENTS = 32;
using Entity = std::uint32_t;
template <typename T>
class ComponentArray
{
public:
void addComponentToArray(const Entity entity, T &&component)
{
assert(not entityFound(entity));
const auto newIndex = componentArraySize_;
entityToIndex_[entity] = newIndex;
indexToEntity_[newIndex] = entity;
componentArray_[newIndex] = std::move(component);
++componentArraySize_;
}
private:
bool entityFound(const Entity entity) const
{
return entityToIndex_.find(entity) != entityToIndex_.cend();
}
private:
std::size_t componentArraySize_ = 0;
std::array<T, MAX_COMPONENTS> componentArray_;
std::unordered_map<Entity, std::size_t> entityToIndex_;
std::unordered_map<std::size_t, Entity> indexToEntity_;
};
以下是使用ComponentArray 功能的函数
struct Transform
{
int x = 0;
};
void test0()
{
ComponentArray<Transform> carr{};
constexpr int entitiesCount = 32;
for (int i = 0; i < entitiesCount; ++i)
carr.addComponentToArray(i, Transform{});
}
注意entitiesCount的值是32,对于0-32的范围,代码将按预期工作,即没有报告SIGSEGV。然而,如果我将其更改为33(或任何高于33的值(,它将崩溃。这是valgrind报告:
==17193== Invalid write of size 8
==17193== at 0x484F7ED: memset (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17193== by 0x109EE0: std::_Hashtable<unsigned int, std::pair<unsigned int const, unsigned long>, std::allocator<std::pair<unsigned int const, unsigned long> >, std::__detail::_Select1st, std::equal_to<unsigned int>, std::hash<unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::clear() (in /home/devel/cpp/draft/test4)
==17193== by 0x109E48: std::_Hashtable<unsigned int, std::pair<unsigned int const, unsigned long>, std::allocator<std::pair<unsigned int const, unsigned long> >, std::__detail::_Select1st, std::equal_to<unsigned int>, std::hash<unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::~_Hashtable() (in /home/devel/cpp/draft/test4)
==17193== by 0x1097B4: std::unordered_map<unsigned int, unsigned long, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, unsigned long> > >::~unordered_map() (in /home/devel/cpp/draft/test4)
==17193== by 0x10949F: ComponentArray<Transform>::~ComponentArray() (in /home/devel/cpp/draft/test4)
==17193== by 0x1092C7: test0() (in /home/devel/cpp/draft/test4)
==17193== by 0x109308: main (in /home/devel/cpp/draft/test4)
==17193== Address 0x0 is not stack'd, malloc'd or (recently) free'd
==17193==
==17193==
==17193== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==17193== Access not within mapped region at address 0x0
==17193== at 0x484F7ED: memset (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17193== by 0x109EE0: std::_Hashtable<unsigned int, std::pair<unsigned int const, unsigned long>, std::allocator<std::pair<unsigned int const, unsigned long> >, std::__detail::_Select1st, std::equal_to<unsigned int>, std::hash<unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::clear() (in /home/devel/cpp/draft/test4)
==17193== by 0x109E48: std::_Hashtable<unsigned int, std::pair<unsigned int const, unsigned long>, std::allocator<std::pair<unsigned int const, unsigned long> >, std::__detail::_Select1st, std::equal_to<unsigned int>, std::hash<unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::~_Hashtable() (in /home/devel/cpp/draft/test4)
==17193== by 0x1097B4: std::unordered_map<unsigned int, unsigned long, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, unsigned long> > >::~unordered_map() (in /home/devel/cpp/draft/test4)
==17193== by 0x10949F: ComponentArray<Transform>::~ComponentArray() (in /home/devel/cpp/draft/test4)
==17193== by 0x1092C7: test0() (in /home/devel/cpp/draft/test4)
==17193== by 0x109308: main (in /home/devel/cpp/draft/test4)
==17193== If you believe this happened as a result of a stack
==17193== overflow in your program's main thread (unlikely but
==17193== possible), you can try to increase the size of the
==17193== main thread stack using the --main-stacksize= flag.
==17193== The main thread stack size used in this run was 8388608.
==17193==
==17193== HEAP SUMMARY:
==17193== in use at exit: 73,176 bytes in 2 blocks
==17193== total heap usage: 73 allocs, 71 frees, 75,904 bytes allocated
==17193==
==17193== LEAK SUMMARY:
==17193== definitely lost: 472 bytes in 1 blocks
==17193== indirectly lost: 0 bytes in 0 blocks
==17193== possibly lost: 0 bytes in 0 blocks
==17193== still reachable: 72,704 bytes in 1 blocks
==17193== suppressed: 0 bytes in 0 blocks
==17193== Rerun with --leak-check=full to see details of leaked memory
==17193==
==17193== For lists of detected and suppressed errors, rerun with: -s
==17193== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)
但它还没有结束。另一个惊喜是,如果我做出改变,
struct Transform
{
// no members
};
然后我可以使entitesCount为任何数字(当然gr_eq大于0(,代码就会正常工作。这对我来说真的很困惑,因为ComponentArray内部的unordered_map将std::uint32_t映射到size_t,反之亦然,那么具有简单int值(或任何其他类型(的t=Component如何与此错误相关。直到今天,我很少使用undered_map,所以我相信我在这里缺少了一些东西。有人能给我一些如何处理这个问题的建议吗?
谨致问候,0x707
正如@RetiredNinja在评论中所观察到的,当添加超过32个元素时,我忘记了MAX_COMPONENTS限制更新。