首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >gdb调试std::list<> -如何遍历列表?

gdb调试std::list<> -如何遍历列表?
EN

Stack Overflow用户
提问于 2022-07-05 15:37:54
回答 1查看 190关注 0票数 1

std::list<>用于遍历的奇怪内部布局是什么?

当我使用像这样的python助手时:

代码语言:javascript
运行
复制
def invokeCore(self, headlines, data, valueType, valueHandler):
    
    valueType = gdb.lookup_type (valueType)
    
    head = data['_M_impl']['_M_node'].address
    current = data['_M_impl']['_M_node']['_M_next']
    
    no = 0
    while current != head:
        realData = current.cast(valueType)
        if realData.address != 0:
            valueHandler(no, realData)
        else:
            print('### PrintGenericList::invokeCore(): failed downcast for list node')

        current = current['_M_next']
        no += 1

    return ''

然后回调'valueHandler‘打印的第一个条目为空,最后一个条目丢失。

根据这里的另一个参考:https://github.com/johnathan79717/gdb-stl-view/blob/master/.gdbinit

有用

代码语言:javascript
运行
复制
p **($arg1**)($current + 1)

但这是什么($current + 1)呢?

初始化$current时

代码语言:javascript
运行
复制
set $current = $arg0._M_impl._M_node._M_next
EN

回答 1

Stack Overflow用户

发布于 2022-07-06 11:25:03

首先,链接到的宏不再可靠。我使用std::list和GCC 9.3.1进行了测试,plist不能正常工作,所以不要太依赖它们来提供指导。

然而,这些宏试图做的是绕过用于在列表中存储数据的类型。

考虑一下,这个GDB会话中my_list是一个包含3个元素的std::list<int>

代码语言:javascript
运行
复制
(gdb) p/r my_list
$1 = {
  <std::__cxx11::_List_base<int, std::allocator<int> >> = {
    _M_impl = {
      <std::allocator<std::_List_node<int> >> = {
        <__gnu_cxx::new_allocator<std::_List_node<int> >> = {<No data fields>}, <No data fields>}, 
      members of std::__cxx11::_List_base<int, std::allocator<int> >::_List_impl:
      _M_node = {
        <std::__detail::_List_node_base> = {
          _M_next = 0x416eb0,
          _M_prev = 0x416ef0
        }, 
        members of std::__detail::_List_node_header:
        _M_size = 3
      }
    }
  }, <No data fields>}
(gdb) ptype my_list._M_impl._M_node 
type = struct std::__detail::_List_node_header : public std::__detail::_List_node_base {
  public:
    std::size_t _M_size;

    _List_node_header(void);
    _List_node_header(std::__detail::_List_node_header &&);
    void _M_move_nodes(std::__detail::_List_node_header &&);
    void _M_init(void);
  private:
    std::__detail::_List_node_base * _M_base(void);
}
(gdb) ptype my_list._M_impl._M_node._M_next 
type = struct std::__detail::_List_node_base {
    std::__detail::_List_node_base *_M_next;
    std::__detail::_List_node_base *_M_prev;
  public:
    static void swap(std::__detail::_List_node_base &, std::__detail::_List_node_base &);
    void _M_transfer(std::__detail::_List_node_base *, std::__detail::_List_node_base *);
    void _M_reverse(void);
    void _M_hook(std::__detail::_List_node_base *);
    void _M_unhook(void);
} *
(gdb) p *my_list._M_impl._M_node._M_next 
$7 = {
  _M_next = 0x416ed0,
  _M_prev = 0x7fffffffad40
}

因此,_M_node是一个_List_node_header,它链接到一个_List_node_base,它链接到进一步的_List_node_base,直到我们最终链接回_M_node,这表明列表的结束。

您可以在代码中使用while current != head:循环来处理这个问题。

那么问题是,数据存储在哪里呢?原来,_M_next指出的东西也不是_List_node_base对象,而是std::__cxx11::list<int, std::allocator<int> >::_Node,它看起来如下所示:

代码语言:javascript
运行
复制
(gdb) ptype std::__cxx11::list<int, std::allocator<int> >::_Node
type = struct std::_List_node<int> [with _Tp = int] : public std::__detail::_List_node_base {
    __gnu_cxx::__aligned_membuf<_Tp> _M_storage;
  public:
    _Tp * _M_valptr(void);
    const _Tp * _M_valptr(void) const;
}

注意,这是_List_node_base的一个子类。因此,如果我们加入一个适当的演员阵容,我们可以看到以下内容:

代码语言:javascript
运行
复制
(gdb) p *(std::__cxx11::list<int, std::allocator<int> >::_Node *) my_list._M_impl._M_node._M_next 
$2 = {
  <std::__detail::_List_node_base> = {
    _M_next = 0x416ed0,
    _M_prev = 0x7fffffffad40
  }, 
  members of std::_List_node<int>:
  _M_storage = {
    _M_storage = "\003\000\000"
  }
}

现在,当您链接的GDB宏说$current + 1 $current是一个_M_next指针,并且是_List_node_base类型时,情况变得更加清楚了。+ 1将跳过一个跨越_M_next_M_prev指针的_List_node_base的大小,然后从子类中查看_M_storage

然后,**($arg1**)($current + 1)只将_M_storage当作指向列表元素类型指针的指针,然后尝试取消引用并获取列表值。这一点不再适用于我,因为int值直接存储在_M_storage缓冲区中,而不是将指向对象的指针放在缓冲区中,在这里GCC似乎比以前更理想,但这意味着您需要更聪明地从列表中获取值。

这就是为什么我一开始就说那些宏是个陷阱。上述任何一个都不是真正可靠的。我注意到,宏中的评论谈到GCC 4.x,所以这可能在当时看起来并不那么糟糕,但现在真的不太好了。

那么,接下来你该怎么办呢?那么,对于打印std::清单,我建议尝试使用GCC现有的漂亮打印机。大多数发行版应该有一个包含这些包的包,GDB应该自动加载它们。有了这些措施之后:

代码语言:javascript
运行
复制
(gdb) p my_list
$14 = std::__cxx11::list = {
  [0] = 3,
  [1] = 4,
  [2] = 5
}

但是,如果您真的想学习如何遍历STL数据结构进行打印,那么我建议使用你读过GCC为他们漂亮的打印机提供的资料,这些应该会提供更可靠的知识来源。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72872113

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档