朝花夕拾系列缘起
时光荏苒,转眼已经毕业三年多了,而在这三年中,自己得过且过,在技术上45并无长足的长进,并且还把以往学过的知识忘记的差不多了,因此有了这个朝花夕拾的系列。寄希望于在写这些文档的同时,加深自己对C++技术的认识,并且建立梳理下自己的知识体系。
今天算是这个系列中的第一期,先剖析下C++ STL 的string类的内存增长策略,这个问题经常在面试的时候被提及,我一般都是回答,没研究过,不过应该是倍增的,也不知道面试官想要的答案是什么,自己也没深究过。没辙了,自己研究下吧。 先上一段测试代码:
-
// 测试字符串增长策略
-
void testStringIncrease() {
-
string strTest = "123";
-
printf("str capacity %d, size %d\n", strTest.capacity(), strTest.size());
-
strTest += "456";
-
printf("str capacity %d, size %d\n", strTest.capacity(), strTest.size());
-
strTest += "456";
-
printf("str capacity %d, size %d\n", strTest.capacity(), strTest.size());
-
strTest += "456";
-
printf("str capacity %d, size %d\n", strTest.capacity(), strTest.size());
-
strTest += "456";
-
printf("str capacity %d, size %d\n", strTest.capacity(), strTest.size());
-
strTest += "1234567890";
-
printf("str capacity %d, size %d\n", strTest.capacity(), strTest.size());
-
}
我们知道C++ string的设计中使用了两个属性capacity和size,分别来表示string的容量和实际占用的空间大小。string的内存都是动态增长的,因此可以猜测当size的大小要超过capacity时肯定要增加内存以容纳更多的字符。但是一次增长多少呢?又是按照怎么样的策略进行增长的呢?是否是和我上面所说的倍增增长呢?下面我们一起去揭露string内存增长的神秘面纱。
环境:VS2008 SP1
测试代码上面已经写了。下面使用VS2008的断点功能调试下。
首先我们打上断点
执行进入STL源码
构造的时候先调用_Tidy方法,将成员变量_Myres = _BUF_SIZE - 1;
然后调用assign方法将传进来的字符串进行赋值
这里调用了STL的assign方法,初始构造容量肯定是0,因此需要增加3个字节的空间,调用 _Grow方法
上面已经赋值为15了而从0增长到3不用新分配内存,因此。这里全部调过。
接下来大同小异,我们直接跳到这一行代码,因为这一行代码要重新分配内存了。
同样的流程,这一直跳到 _Grow方法,出现了不同,此时【_Myres < _Newsize】条件满足,开始执行_Copy方法
然后我们苦苦追寻的亮点出现了
他的内存增长是在分配了足够的内存的基础上再和一个掩码进行|操作,这个掩码我们可以看到是15。因此这个就算是VS 2008下STL string的内存增长策略了。
而且我发现了一个亮点在VS下不同大小的字符串是存放在不同的位置的
在取字符串地址的时候我们都用的是_Myptr方法,而这个方法会根据当前字符串的容量返回两个不同的值,这两个值一个是数组存储的一个是指针,很明显可以得到结论,当字符串长度小于等于15的时候是存放在栈里的,而当字符串长度超过15到时候,又会转移到堆里面。
来源:oschina
链接:https://my.oschina.net/u/4000302/blog/4469522