c++的map的内存布局

以下均基于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字节。

你可能感兴趣的:(标准C/C++,c++,算法)