所以sizeof(list
,两个指针。每一个真正的节点首先是包含两个指针,然后是元素内容(_List_node)。
通过代码输出list的内容:
#define NEXT(ptr, T) do { \
void *n = *(char**)ptr; \
T val = *(T*)((char**)ptr + 2); \
printf("list item %p val: 0x%x\n", ptr, val); \
ptr = n; \
} while (0)
template
void ds_list_i(void *p) {
void *ptr = *(char**)p;
NEXT(ptr, T);
NEXT(ptr, T);
NEXT(ptr, T);
}
size_t ds_list() {
std::list
lst; lst.push_back(0x11); lst.push_back(0x22); lst.push_back(0x33); ds_list_i
(&lst); return lst.size(); }
在gdb中可以以下方式遍历该list:
(gdb) p p $4 = (void *) 0x7fffffffe390 (gdb) x/1a p 0x7fffffffe390: 0x606080 (gdb) x/1xw 0x606080+16 # 元素1 0x606090: 0x00000011 (gdb) x/1a 0x606080 0x606080: 0x6060a0 (gdb) x/1xw 0x6060a0+16 # 元素2 0x6060b0: 0x00000022
map
map使用的是红黑树实现,实际使用的是stl_tree.h实现:
bits/stl_map.h
typedef _Rb_tree, key_compare, _Pair_alloc_type> _Rep_type; ... _Rep_type _M_t; ... iterator begin() { return _M_t.begin(); }
bits/stl_tree.h
struct _Rb_tree_node_base
{
typedef _Rb_tree_node_base* _Base_ptr;
typedef const _Rb_tree_node_base* _Const_Base_ptr;
_Rb_tree_color _M_color;
_Base_ptr _M_parent;
_Base_ptr _M_left;
_Base_ptr _M_right;
...
};
template
struct _Rb_tree_node : public _Rb_tree_node_base
{
typedef _Rb_tree_node<_Val>* _Link_type;
_Val _M_value_field;
};
template
::__value> struct _Rb_tree_impl : public _Node_allocator { _Key_compare _M_key_compare; _Rb_tree_node_base _M_header; size_type _M_node_count; // Keeps track of size of tree. ... } _Rb_tree_impl<_Compare> _M_impl; ... iterator begin() { return iterator(static_cast<_Link_type> (this->_M_impl._M_header._M_left)); }
所以可以看出,大部分时候(取决于_M_key_compare) sizeof(map
,主要的元素是:
_Rb_tree_color _M_color; // 节点颜色
_Base_ptr _M_parent; // 父节点
_Base_ptr _M_left; // 左节点
_Base_ptr _M_right; // 右节点
_Val _M_value_field // 同list中节点技巧一致,后面是实际的元素
同list中的实现一致,map本身作为一个节点,其不是一个存储数据的节点,
_Rb_tree::end
iterator
end()
{ return iterator(static_cast<_Link_type>(&this->_M_impl._M_header)); }
由于节点值在_Rb_tree_node_base后,所以任意时候拿到节点就可以偏移这个结构体拿到节点值,节点的值是一个pair,包含了key和value。
在gdb中打印以下map的内容:
size_t ds_map() {
std::map
imap;
imap["abc"] = 0xbbb;
return imap.size();
}
(gdb) p/x &imap $7 = 0x7fffffffe370 (gdb) x/1a (char*)&imap+24 # _M_left 真正的节点 0x7fffffffe388: 0x606040 (gdb) x/1xw 0x606040+32+8 # 偏移32字节是节点值的地址,再偏移8则是value的地址 0x606068: 0x00000bbb (gdb) p *(char**)(0x606040+32) # 偏移32字节是string的地址 $8 = 0x606028 "abc"
或者很多时候没有必要这么装逼+蛋疼:
(gdb) p *(char**)(imap._M_t._M_impl._M_header._M_left+1) $9 = 0x606028 "abc" (gdb) x/1xw (char*)(imap._M_t._M_impl._M_header._M_left+1)+8 0x606068: 0x00000bbb
完