以下均基于x86平台64位centos7。
有一个指针偶尔会置成0xffffffff,大佬查了几天发现是由于对map的end迭代器进行了错误操作导致的。简化代码如下:
struct s_t
{
std::map<long, int> m;
void *p;
};
int main()
{
s_t s;
auto it = s.m.end();
it->second = -1;
printf("%p\n", s.p); // 0xffffffff
return 0;
}
要理解具体缘由,先要了解map的内存结构。map的声明是:
template <typename _Key, typename _Tp, typename _Compare = std::less<_Key>,
typename _Alloc = std::allocator<std::pair<const _Key, _Tp> > >
class map;
具体的实现委托是:
struct _Rb_tree_impl : public _Node_allocator
, public _Rb_tree_key_compare<_Compare>
, public _Rb_tree_header
如果使用默认的内存分配器,_Node_allocator里没有成员变量,不占空间
_Rb_tree_key_compare里有一个_Compare类型的成员变量,std::less没有成员变量,_Rb_tree_key_compare
的大小为1
_Rb_tree_header的成员变量有:
_Rb_tree_node_base _M_header; // 指向红黑树
size_t _M_node_count; // 节点数目
红黑树的一个节点包括header(_Rb_tree_node_base类型)和数据(包括_Key和_Tp)两部分,_Rb_tree_node_base的成员变量有:
_Rb_tree_color _M_color; // 枚举类型,表示节点是红还是黑
_Rb_tree_node_base* _M_parent; // 指向红黑树根节点
_Rb_tree_node_base* _M_left; // 指向红黑树最左节点
_Rb_tree_node_base* _M_right; // 指向红黑树最右节点
当红黑树为空时,_M_parent为空,_M_left和_M_right都指向_M_header自身,可通过如下代码验证:
std::map<long, int> m;
auto it = m.end();
std::_Rb_tree_node_base *p = (std::_Rb_tree_node_base*)&it->first;
--p;
cout << p << endl;
cout << p->_M_parent << endl;
cout << p->_M_left << endl;
cout << p->_M_right << endl;
所以map
的内存布局是:
std::less
rb_head
color
parent
left
right
count
内存对齐后共占用48字节的空间,但如果指定1字节对齐后,总共占用37字节,可通过如下代码输出:
#include
#pragma pack(1)
#include
int main()
{
std::cout << sizeof(std::less<int>) << std::endl; //1
std::cout << sizeof(std::_Rb_tree_color) << std::endl; //4
std::cout << sizeof(std::_Rb_tree_node_base) << std::endl; //28
std::cout << sizeof(size_t) << std::endl; //8
std::cout << sizeof(std::map<int, int>) << std::endl; //37
return 0;
}
最后end()指向的是_M_header,其数据部分正好从_M_node_count的位置开始,it->first正好是_M_node_count的位置,it->second则是p的低4字节。