• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            loop_in_codes

            低調(diào)做技術(shù)__歡迎移步我的獨(dú)立博客 codemaro.com 微博 kevinlynx

            基于內(nèi)存查看STL常用容器內(nèi)容

            有時(shí)候在線上使用gdb調(diào)試程序core問(wèn)題時(shí),可能沒(méi)有符號(hào)文件,拿到的僅是一個(gè)內(nèi)存地址,如果這個(gè)指向的是一個(gè)STL對(duì)象,那么如何查看這個(gè)對(duì)象的內(nèi)容呢?

            只需要知道STL各個(gè)容器的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn),就可以查看其內(nèi)容。本文描述了SGI STL實(shí)現(xiàn)中常用容器的數(shù)據(jù)結(jié)構(gòu),以及如何在gdb中查看其內(nèi)容。

            string

            string,即basic_string bits/basic_string.h

            mutable _Alloc_hider  _M_dataplus;
                ... 
                  const _CharT*
                  c_str() const
                  { return _M_data(); }
                ...    
                  _CharT*
                  _M_data() const 
                  { return  _M_dataplus._M_p; }
            
                ...
                  struct _Alloc_hider : _Alloc
                  {
                _Alloc_hider(_CharT* __dat, const _Alloc& __a)
                : _Alloc(__a), _M_p(__dat) { }
            
                _CharT* _M_p; // The actual data.
                  };
               
                  size_type
                  length() const
                  { return _M_rep()->_M_length; }
            
                  _Rep*
                  _M_rep() const
                  { return &((reinterpret_cast<_Rep*> (_M_data()))[-1]); }
            
                  ...
                   struct _Rep_base
                  {
                size_type       _M_length;
                size_type       _M_capacity;
                _Atomic_word        _M_refcount;
                  };
            
                  struct _Rep : _Rep_base

            即,string內(nèi)有一個(gè)指針,指向?qū)嶋H的字符串位置,這個(gè)位置前面有一個(gè)_Rep結(jié)構(gòu),其內(nèi)保存了字符串的長(zhǎng)度、可用內(nèi)存以及引用計(jì)數(shù)。當(dāng)我們拿到一個(gè)string對(duì)象的地址時(shí),可以通過(guò)以下代碼獲取相關(guān)值:

            void ds_str_i(void *p) {
                    char **raw = (char**)p;
                    char *s = *raw;
                    size_t len = *(size_t*)(s - sizeof(size_t) * 3);
                    printf("str: %s (%zd)\n", s, len);
                }
            
                size_t ds_str() {
                    std::string s = "hello";
                    ds_str_i(&s);
                    return s.size();
                }

            在gdb中拿到一個(gè)string的地址時(shí),可以以下打印出該字符串及長(zhǎng)度:

            (gdb) x/1a p
            0x7fffffffe3a0: 0x606028
            (gdb) p (char*)0x606028
            $2 = 0x606028 "hello"
            (gdb) x/1dg 0x606028-24
            0x606010:       5
            

            vector

            眾所周知vector實(shí)現(xiàn)就是一塊連續(xù)的內(nèi)存,bits/stl_vector.h

            template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
                class vector : protected _Vector_base<_Tp, _Alloc>
            
                ...
                template<typename _Tp, typename _Alloc>
                struct _Vector_base
                {
                  typedef typename _Alloc::template rebind<_Tp>::other _Tp_alloc_type;
            
                  struct _Vector_impl
                  : public _Tp_alloc_type
                  {
                _Tp*           _M_start;
                _Tp*           _M_finish;
                _Tp*           _M_end_of_storage;
                _Vector_impl(_Tp_alloc_type const& __a)
                : _Tp_alloc_type(__a), _M_start(0), _M_finish(0), _M_end_of_storage(0)
                { }
                  };
            
            
                  _Vector_impl _M_impl;

            可以看出sizeof(vector<xxx>)=24,其內(nèi)也就是3個(gè)指針,_M_start指向首元素地址,_M_finish指向最后一個(gè)節(jié)點(diǎn)+1,_M_end_of_storage是可用空間最后的位置。

            iterator
                  end()
                  { return iterator (this->_M_impl._M_finish); }
                  const_iterator
                  ...
                  begin() const
                  { return const_iterator (this->_M_impl._M_start); }
                  ...
                  size_type
                  capacity() const
                  { return size_type(const_iterator(this->_M_impl._M_end_of_storage)
                         - begin()); }

            可以通過(guò)代碼從一個(gè)vector對(duì)象地址輸出其信息:

            template <typename T>
                void ds_vec_i(void *p) {
                    T *start = *(T**)p;
                    T *finish = *(T**)((char*)p + sizeof(void*));
                    T *end_storage = *(T**)((char*)p + 2 * sizeof(void*));
                    printf("vec size: %ld, avaiable size: %ld\n", finish - start, end_storage - start); 
                }
            
                size_t ds_vec() {
                    std::vector<int> vec;
                    vec.push_back(0x11);
                    vec.push_back(0x22);
                    vec.push_back(0x33);
                    ds_vec_i<int>(&vec);
                    return vec.size();
                }

            使用gdb輸出一個(gè)vector中的內(nèi)容:

            (gdb) p p
            $3 = (void *) 0x7fffffffe380
            (gdb) x/1a p
            0x7fffffffe380: 0x606080
            (gdb) x/3xw 0x606080
            0x606080:       0x00000011      0x00000022      0x00000033
            

            list

            眾所周知list被實(shí)現(xiàn)為一個(gè)鏈表。準(zhǔn)確來(lái)說(shuō)是一個(gè)雙向鏈表。list本身是一個(gè)特殊節(jié)點(diǎn),其代表end,其指向的下一個(gè)元素才是list真正的第一個(gè)節(jié)點(diǎn):

            bits/stl_list.h

            bool
                  empty() const
                  { return this->_M_impl._M_node._M_next == &this->_M_impl._M_node; }
            
                  const_iterator
                  begin() const
                  { return const_iterator(this->_M_impl._M_node._M_next); }
            
                  iterator
                  end()
                  { return iterator(&this->_M_impl._M_node); }
            
                  ...
            
                struct _List_node_base
                {
                    _List_node_base* _M_next;   ///< Self-explanatory
                    _List_node_base* _M_prev;   ///< Self-explanatory
                    ...
                };
                     
                template<typename _Tp>
                struct _List_node : public _List_node_base
                {
                  _Tp _M_data;                ///< User's data.
                };
                  
                template<typename _Tp, typename _Alloc>
                class _List_base
                {
                    ...
                  struct _List_impl
                  : public _Node_alloc_type
                  {
                _List_node_base _M_node;
                    ...
                  };
            
                  _List_impl _M_impl;
            
                      
                template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
                class list : protected _List_base<_Tp, _Alloc>

            所以sizeof(list<xx>)=16,兩個(gè)指針。每一個(gè)真正的節(jié)點(diǎn)首先是包含兩個(gè)指針,然后是元素內(nèi)容(_List_node)。

            通過(guò)代碼輸出list的內(nèi)容:

            #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 <typename T>
                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<int> lst;
                    lst.push_back(0x11);
                    lst.push_back(0x22);
                    lst.push_back(0x33);
                    ds_list_i<int>(&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使用的是紅黑樹實(shí)現(xiàn),實(shí)際使用的是stl_tree.h實(shí)現(xiàn):

            bits/stl_map.h

            typedef _Rb_tree<key_type, value_type, _Select1st<value_type>,
                           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<typename _Val>
                struct _Rb_tree_node : public _Rb_tree_node_base
                {
                  typedef _Rb_tree_node<_Val>* _Link_type;
                  _Val _M_value_field;
                };
            
            
                template<typename _Key_compare,
                       bool _Is_pod_comparator = std::__is_pod<_Key_compare>::__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));
                  }

            所以可以看出,大部分時(shí)候(取決于_M_key_compare) sizeof(map<xx>)=48,主要的元素是:

            _Rb_tree_color  _M_color; // 節(jié)點(diǎn)顏色
                    _Base_ptr       _M_parent; // 父節(jié)點(diǎn)
                    _Base_ptr       _M_left; // 左節(jié)點(diǎn)
                    _Base_ptr       _M_right; // 右節(jié)點(diǎn)
                    _Val            _M_value_field // 同list中節(jié)點(diǎn)技巧一致,后面是實(shí)際的元素

            同list中的實(shí)現(xiàn)一致,map本身作為一個(gè)節(jié)點(diǎn),其不是一個(gè)存儲(chǔ)數(shù)據(jù)的節(jié)點(diǎn),

            _Rb_tree::end

            iterator
                  end()
                  { return iterator(static_cast<_Link_type>(&this->_M_impl._M_header)); }

            由于節(jié)點(diǎn)值在_Rb_tree_node_base后,所以任意時(shí)候拿到節(jié)點(diǎn)就可以偏移這個(gè)結(jié)構(gòu)體拿到節(jié)點(diǎn)值,節(jié)點(diǎn)的值是一個(gè)pair,包含了key和value。

            在gdb中打印以下map的內(nèi)容:

            size_t ds_map() {
                    std::map<std::string, int> imap;
                    imap["abc"] = 0xbbb;
                    return imap.size();
                }
            (gdb) p/x &imap
            $7 = 0x7fffffffe370
            (gdb) x/1a (char*)&imap+24       # _M_left 真正的節(jié)點(diǎn)
            0x7fffffffe388: 0x606040          
            (gdb) x/1xw 0x606040+32+8        # 偏移32字節(jié)是節(jié)點(diǎn)值的地址,再偏移8則是value的地址
            0x606068:       0x00000bbb
            (gdb) p *(char**)(0x606040+32)   # 偏移32字節(jié)是string的地址
            $8 = 0x606028 "abc"
            

            或者很多時(shí)候沒(méi)有必要這么裝逼+蛋疼:

            (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
            

            posted on 2014-12-03 22:08 Kevin Lynx 閱讀(3838) 評(píng)論(2)  編輯 收藏 引用 所屬分類: c/c++

            評(píng)論

            # re: 基于內(nèi)存查看STL常用容器內(nèi)容 2014-12-04 18:57 xxoo

            問(wèn)一下,既然string的結(jié)構(gòu)類型是
            (size_t,size_t,size_t,char*)
            為啥string地址里前4個(gè)自己是char*的地址,應(yīng)該是size_t的前4個(gè)字節(jié)才對(duì)啊?  回復(fù)  更多評(píng)論   

            # re: 基于內(nèi)存查看STL常用容器內(nèi)容 2014-12-07 18:27 Kevin Lynx

            @xxoo
            "即,string內(nèi)有一個(gè)指針,指向?qū)嶋H的字符串位置,這個(gè)位置前面有一個(gè)_Rep結(jié)構(gòu),其內(nèi)保存了字符串的長(zhǎng)度、可用內(nèi)存以及引用計(jì)數(shù)。"  回復(fù)  更多評(píng)論   

            久久99热这里只有精品国产| 成人久久综合网| 久久中文字幕人妻熟av女| 亚洲中文字幕久久精品无码APP | 日产精品99久久久久久| 久久久久久久97| 久久福利片| avtt天堂网久久精品| 久久久久久久综合综合狠狠| 久久久无码精品亚洲日韩按摩| 很黄很污的网站久久mimi色| 国内精品久久久久久久久电影网| 青青草原综合久久| 青青草国产97免久久费观看| 热久久国产欧美一区二区精品| 亚洲AV无码久久| 国产激情久久久久影院小草| 久久综合国产乱子伦精品免费| 无码乱码观看精品久久| 国产精品青草久久久久福利99| 久久影院综合精品| 欧美亚洲国产精品久久| 久久久噜噜噜久久| 国产成人精品久久| 久久九九有精品国产23百花影院| 日本五月天婷久久网站| 亚洲国产高清精品线久久| 久久国产精品免费| 国产69精品久久久久99| 欧美精品一本久久男人的天堂| 狠狠88综合久久久久综合网| 久久久久亚洲精品无码蜜桃| 日产精品久久久久久久性色| 色偷偷偷久久伊人大杳蕉| 国产亚洲精品久久久久秋霞| 久久久亚洲裙底偷窥综合| 亚洲乱码精品久久久久..| 一本色道久久HEZYO无码| 色狠狠久久AV五月综合| 青草国产精品久久久久久| 久久久久人妻精品一区|