手把手教你调试STL容器(上)
来源:互联网 发布:nginx 目录映射 编辑:程序博客网 时间:2024/05/22 15:27
众所周知,调试(Debugging)是每个程序员所要必备的基本的技术素养,尤其是对C/C++的程序员来说。对于在linux下用C/C++开发的朋友,相信对GDB不会陌生,当程序有bug或者是出现core dump的时候,GDB是我们最好的朋友。STL是C++相较于C而言,增加的非常强有力的工具,它从某种程度上把C/C++程序员从繁琐的基本数据结构中解放了出来。不过,STL虽然用起来十分方便,但是,用GDB调试过C/C++程序的朋友都有这样痛苦的经历,在GDB状态下,要知道某个STL对象(比如容器)中的数据内容,并不是那么直接、简单。本文的主要内容就是介绍STL中大家比较常用到的容器的基本组成,帮助大家能够在调试的时候更好的驾驭它们。
string是STL中最为常用的类型,它是模板类basic_string用char类型特化后的结果,下面我们来看一下string类型的基本组成:
01
<SPAN
class
=kwd>
typedef
</SPAN><SPAN
class
=pln> basic_string</SPAN><SPAN
class
=str><
char
></SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=kwd>string</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
02
03
</SPAN><SPAN
class
=kwd>
struct
</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=typ>_Rep_base</SPAN><SPAN
class
=pln>
04
</SPAN><SPAN
class
=pun>{</SPAN><SPAN
class
=pln>
05
size_type _M_length</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
06
size_type _M_capacity</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
07
</SPAN><SPAN
class
=typ>_Atomic_word</SPAN><SPAN
class
=pln> _M_refcount</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
08
</SPAN><SPAN
class
=pun>};</SPAN><SPAN
class
=pln>
09
10
</SPAN><SPAN
class
=kwd>
struct
</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=typ>_Alloc_hider</SPAN><SPAN
class
=pln>
11
</SPAN><SPAN
class
=pun>{</SPAN><SPAN
class
=pln>
12
</SPAN><SPAN
class
=typ>_CharT</SPAN><SPAN
class
=pun>*</SPAN><SPAN
class
=pln> _M_p</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
13
</SPAN><SPAN
class
=pun>};</SPAN><SPAN
class
=pln>
14
15
</SPAN><SPAN
class
=kwd>
mutable
</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=typ>_Alloc_hider</SPAN><SPAN
class
=pln> _M_dataplus</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
16
_M_data</SPAN><SPAN
class
=pun>()</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=kwd>
const
</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=pun>{</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=kwd>
return
</SPAN><SPAN
class
=pln> _M_dataplus</SPAN><SPAN
class
=pun>.</SPAN><SPAN
class
=pln>_M_p</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=pun>}</SPAN><SPAN
class
=pln>
17
</SPAN><SPAN
class
=typ>_Rep</SPAN><SPAN
class
=pun>*</SPAN><SPAN
class
=pln> _M_rep</SPAN><SPAN
class
=pun>()</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=kwd>
const
</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=pun>{</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=kwd>
return
</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=pun>&((</SPAN><SPAN
class
=kwd>
reinterpret_cast
</SPAN><SPAN
class
=pun><</SPAN><SPAN
class
=pln>_rep </SPAN><SPAN
class
=pun>*></SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=pun>(</SPAN><SPAN
class
=pln>_M_data</SPAN><SPAN
class
=pun>()))[-</SPAN><SPAN
class
=lit>1</SPAN><SPAN
class
=pun>]);</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=pun>}</SPAN>
从上面来看,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()函数来查看。
vector同样也是stl中最为常用类型,下面我们来看一下vector类型的基本组成:
1
<SPAN
class
=kwd>
struct
</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=typ>_Vector_impl</SPAN><SPAN
class
=pln>
2
</SPAN><SPAN
class
=pun>{</SPAN><SPAN
class
=pln>
3
</SPAN><SPAN
class
=typ>_Tp</SPAN><SPAN
class
=pun>*</SPAN><SPAN
class
=pln> _M_start</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
4
</SPAN><SPAN
class
=typ>_Tp</SPAN><SPAN
class
=pun>*</SPAN><SPAN
class
=pln> _M_finish</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
5
</SPAN><SPAN
class
=typ>_Tp</SPAN><SPAN
class
=pun>*</SPAN><SPAN
class
=pln> _M_end_of_storage</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
6
</SPAN><SPAN
class
=pun>};</SPAN><SPAN
class
=pln>
7
8
</SPAN><SPAN
class
=typ>_Vector_impl</SPAN><SPAN
class
=pln> _M_impl</SPAN><SPAN
class
=pun>;</SPAN>
vector本身很简单,就是一个动态数组。它只有一个数组成员_M_impl,用户可以通过_M_impl来查看vector内容的数据成员,具体包括动态数组的超始地址_M_impl._M_start,结束地址_M_impl._M_end_of_storage,数组内容的结束地址_M_impl._M_finish
list是STL中的双向链表结构,也是大家经常用来的,下面我们一起来看一下list的基本组成:
01
<SPAN
class
=kwd>
struct
</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=typ>_List_node_base</SPAN><SPAN
class
=pln>
02
</SPAN><SPAN
class
=pun>{</SPAN><SPAN
class
=pln>
03
</SPAN><SPAN
class
=typ>_List_node_base</SPAN><SPAN
class
=pun>*</SPAN><SPAN
class
=pln> _M_next</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
04
</SPAN><SPAN
class
=typ>_List_node_base</SPAN><SPAN
class
=pun>*</SPAN><SPAN
class
=pln> _M_prev</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
05
</SPAN><SPAN
class
=pun>};</SPAN><SPAN
class
=pln>
06
07
</SPAN><SPAN
class
=kwd>
template
</SPAN><SPAN
class
=pun><</SPAN><SPAN
class
=kwd>
typename
</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=typ>_Tp</SPAN><SPAN
class
=pun>></SPAN><SPAN
class
=pln>
08
</SPAN><SPAN
class
=kwd>
struct
</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=typ>_List_node</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=pun>:</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=kwd>
public
</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=typ>_List_node_base</SPAN><SPAN
class
=pln>
09
</SPAN><SPAN
class
=pun>{</SPAN><SPAN
class
=pln>
10
</SPAN><SPAN
class
=typ>_Tp</SPAN><SPAN
class
=pln> _M_data</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
11
</SPAN><SPAN
class
=pun>};</SPAN><SPAN
class
=pln>
12
13
</SPAN><SPAN
class
=kwd>
struct
</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=typ>_List_impl</SPAN><SPAN
class
=pln>
14
</SPAN><SPAN
class
=pun>{</SPAN><SPAN
class
=pln>
15
</SPAN><SPAN
class
=typ>_List_node_base</SPAN><SPAN
class
=pln> _M_node</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
16
</SPAN><SPAN
class
=pun>};</SPAN><SPAN
class
=pln>
17
18
</SPAN><SPAN
class
=typ>_List_impl</SPAN><SPAN
class
=pln> _M_impl</SPAN><SPAN
class
=pun>;</SPAN>
通过上面的代码,我们可以看出,list本身只保留了一个空结点_M_impl._M_node,用来标示list的header,新加入的第一个结点,是直接链到_M_imp._M_node._M_next上的,后面的依次类推。我们可以把_M_impl._M_node.m_next强制转型成_List_node类型,然后通过_M_data来查看具体的数据内容,即((_List_node<T> *)(_M_impl._M_node->M_next))->M_data (T是数据的类型)。
我们知道,string和vector都是线型结构,只要知道数据的起始地址和容器大小,便可获取到所以的元素内容。但是list是非线型结构,这时候就需要iterator来帮忙了,下面我们来看下list的iterator的基本组成:
1
<SPAN
class
=kwd>
template
</SPAN><SPAN
class
=pun><</SPAN><SPAN
class
=kwd>
typename
</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=typ>_Tp</SPAN><SPAN
class
=pun>></SPAN><SPAN
class
=pln>
2
</SPAN><SPAN
class
=kwd>
struct
</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=typ>_List_iterator</SPAN><SPAN
class
=pln>
3
</SPAN><SPAN
class
=pun>{</SPAN><SPAN
class
=pln>
4
</SPAN><SPAN
class
=typ>_List_node_base</SPAN><SPAN
class
=pun>*</SPAN><SPAN
class
=pln> _M_node</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
5
</SPAN><SPAN
class
=pun>};</SPAN>
从上面的代码,我们可以看出,list的iterator只是保存了一个list结点的指针,有此足矣,通过它我们便可以获取到iter里面的数据内容:((_List_node<T> *)(iter->_M_node)->M_data (T是数据的类型)
deque是STL中提供一个队列结构,它同时兼有vector和list的基本特点,因此实现要复杂一些,下面我们来看一下它的基本组成:
01
<SPAN
class
=kwd>
template
</SPAN><SPAN
class
=pun><</SPAN><SPAN
class
=kwd>
typename
</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=typ>_Tp</SPAN><SPAN
class
=pun>></SPAN><SPAN
class
=pln>
02
</SPAN><SPAN
class
=kwd>
struct
</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=typ>_Deque_iterator</SPAN><SPAN
class
=pln>
03
</SPAN><SPAN
class
=pun>{</SPAN><SPAN
class
=pln>
04
</SPAN><SPAN
class
=kwd>
typedef
</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=typ>_Tp</SPAN><SPAN
class
=pun>**</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=typ>_Map_pointer</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
05
06
</SPAN><SPAN
class
=typ>_Tp</SPAN><SPAN
class
=pun>*</SPAN><SPAN
class
=pln> _M_cur</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
07
</SPAN><SPAN
class
=typ>_Tp</SPAN><SPAN
class
=pun>*</SPAN><SPAN
class
=pln> _M_first</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
08
</SPAN><SPAN
class
=typ>_Tp</SPAN><SPAN
class
=pun>*</SPAN><SPAN
class
=pln> _M_last</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
09
</SPAN><SPAN
class
=typ>_Map_pointer</SPAN><SPAN
class
=pln> _M_node</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
10
</SPAN><SPAN
class
=pun>};</SPAN><SPAN
class
=pln>
11
12
</SPAN><SPAN
class
=kwd>
typedef
</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=typ>_Deque_iterator</SPAN><SPAN
class
=pun><</SPAN><SPAN
class
=pln>_tp </SPAN><SPAN
class
=pun>,</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=typ>_Tp</SPAN><SPAN
class
=pun>&,</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=typ>_Tp</SPAN><SPAN
class
=pun>*></SPAN><SPAN
class
=pln> iterator</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
13
</SPAN><SPAN
class
=kwd>
struct
</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=typ>_Deque_impl</SPAN><SPAN
class
=pln>
14
</SPAN><SPAN
class
=pun>:</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=kwd>
public
</SPAN><SPAN
class
=pln> </SPAN><SPAN
class
=typ>_Tp_alloc_type</SPAN><SPAN
class
=pln>
15
</SPAN><SPAN
class
=pun>{</SPAN><SPAN
class
=pln>
16
</SPAN><SPAN
class
=typ>_Tp</SPAN><SPAN
class
=pun>**</SPAN><SPAN
class
=pln> _M_map</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
17
</SPAN><SPAN
class
=typ>
size_t
</SPAN><SPAN
class
=pln> _M_map_size</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
18
iterator _M_start</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
19
iterator _M_finish</SPAN><SPAN
class
=pun>;</SPAN><SPAN
class
=pln>
20
</SPAN><SPAN
class
=pun>};</SPAN><SPAN
class
=pln>
21
</SPAN><SPAN
class
=typ>_Deque_impl</SPAN><SPAN
class
=pln> _M_impl</SPAN><SPAN
class
=pun>;</SPAN>
从上面的代码,及STL的源码,我们可以了解到,deque是由多块连续buffer,通过一个中控的数组_M_map来链在一起,实现的。我们可以通过_M_map,获取到每一个块的起始地址,在每一块内的数据,地址都是连续的,可以像数组一起取出对应的数据内容。
另外,我们还可以通过iterator来访问deque的元素,上面的代码中,iterator的_M_cur是指向当前元素的指针,_M_first是当前块的起始地址,_M_last当前块的结束地址。
本篇是〈手把手教你调试STL容器〉的上篇,主要介绍了string, vector, list和deque这些基本的容器,下篇将介绍map/set/multimap/multiset, hash_map/hash_set/hash_multimap/hash_multiset,敬请期待!
链接地址:http://www.wuzesheng.com/?p=1686
- 手把手教你调试STL容器(上)
- 手把手教你调试STL容器(上)
- 手把手教你调试STL容器(下)
- 手把手教你-gdb调试
- 手把手教你调试Linux C++ 代码
- 手把手教你调试Linux C++ 代码
- GDB调试STL容器
- 手把手教你捕获数据包(上)
- 手把手教你捕获数据包(上)
- 手把手教你appium_mac上环境搭建
- gdb调试-查看STL容器
- GDB调试STL复杂容器
- 使用GDB调试STL容器
- 手把手教你在RAM调试ARM程序
- 全程图解手把手教你做RAID(上)
- 手把手教你如何在XP上建立VPN服务器
- 手把手教你在STM32上移植ZNFAT文件系统
- 手把手教你在Solaris上写一个daemon程序
- 简单的数据存储--Preferences的使用
- 一键退出一个app中所有activity
- 杭电1312HDU acm ---Red and Black---DFS深度优先算法
- Android中 onInterceptTouchEvent, onTouchEvent 理解
- hdu 1069 Monkey and Banana
- 手把手教你调试STL容器(上)
- Android JNI实例代码(二)
- ARM小问题
- cocos2d-x ubuntu开发
- 在eclipse中编译Launcher2的方法
- nslookup用法
- 下载
- wp-admin文件下
- IIS6.0配置支持perl