DSP

手把手教你调试STL容器(上)

2019-07-13 20:46发布

资料出处:http://www.wuzesheng.com/?p=1686 众所周知,调试(Debugging)是每个程序员所要必备的基本的技术素养,尤其是对C/C++的程序员来说。对于在linux下用C/C++开发的朋友,相信对GDB不会陌生,当程序有bug或者是出现core dump的时候,GDB是我们最好的朋友。STL是C++相较于C而言,增加的非常强有力的工具,它从某种程度上把C/C++程序员从繁琐的基本数据结构中解放了出来。不过,STL虽然用起来十分方便,但是,用GDB调试过C/C++程序的朋友都有这样痛苦的经历,在GDB状态下,要知道某个STL对象(比如容器)中的数据内容,并不是那么直接、简单。本文的主要内容就是介绍STL中大家比较常用到的容器的基本组成,帮助大家能够在调试的时候更好的驾驭它们。
  • 1. string string是STL中最为常用的类型,它是模板类basic_string用char类型特化后的结果,下面我们来看一下string类型的基本组成: 01 typedef basic_string<char>    string; 02   03 struct _Rep_base 04 { 05     size_type     _M_length; 06     size_type     _M_capacity; 07     _Atomic_word  _M_refcount; 08 }; 09   10 struct _Alloc_hider 11 { 12     _CharT* _M_p; 13 }; 14   15 mutable _Alloc_hider  _M_dataplus; 16 _M_data() const return  _M_dataplus._M_p; } 17 _Rep* _M_rep() const return &((reinterpret_cast<_rep *> (_M_data()))[-1]); } 从上面来看,string只有一个成员_M_dataplus。但是这里需要注意的是,string类在实现的时候用了比较巧的方法,在_M_dataplus._M_p中保存了用户的数据,在_M_dataplus._M_p的第一个元素前面的位置,保存了string类本身所需要的一些信息rep。这样做的好处,一方面不增加string类的额外开销,另一方面可以保证用户在调试器(GDB)中用_M_dataplus._M_p查看数据内容的时候,不受干扰。用户可以通过reinterpret_cast<_rep *> (_M_data()))[-1])来查看rep相关的信息,也可以调用_M_rep()函数来查看。
  • 2. vector vector同样也是stl中最为常用类型,下面我们来看一下vector类型的基本组成: 1 struct _Vector_impl 2 { 3     _Tp*           _M_start; 4     _Tp*           _M_finish; 5     _Tp*           _M_end_of_storage; 6 }; 7   8 _Vector_impl _M_impl; vector本身很简单,就是一个动态数组。它只有一个数组成员_M_impl,用户可以通过_M_impl来查看vector内容的数据成员,具体包括动态数组的超始地址_M_impl._M_start,结束地址_M_impl._M_end_of_storage,数组内容的结束地址_M_impl._M_finish
  • 3. list list是STL中的双向链表结构,也是大家经常用来的,下面我们一起来看一下list的基本组成: 01 struct _List_node_base 02 { 03     _List_node_base* _M_next;   04     _List_node_base* _M_prev;   05 }; 06   07 template<typename _Tp> 08 struct _List_node : public _List_node_base 09 { 10     _Tp _M_data;               11 }; 12   13 struct _List_impl 14 { 15     _List_node_base _M_node; 16 };