运行这样一段代码做个实验:
char *p = 0; for(int i = 0; i < 40; i += 4) { char* s = new char[i]; printf("alloc %2d bytes, address=%p distance=%d/n", i, s, s - p); p = s; }
我们直接来看VC2005下Release版本的运行结果,DEBUG版因包含了较多的调试信息,这里就不分析了:
alloc 0 bytes, address=003A36F0 distance=3815152
alloc 4 bytes, address=003A3700 distance=16
alloc 8 bytes, address=003A3710 distance=16
alloc 12 bytes, address=003A3720 distance=16
alloc 16 bytes, address=003A3738 distance=24
alloc 20 bytes, address=003A84C0 distance=19848
alloc 24 bytes, address=003A84E0 distance=32
alloc 28 bytes, address=003A8500 distance=32
alloc 32 bytes, address=003A8528 distance=40
alloc 36 bytes, address=003A8550 distance=40
每一次分配的字节数都比上一次多4,distance值记录着与上一次分配的差值,第一个差值没有实际意义,中间有一个较大的差值,可能是这块内存已经被分配了,于是也忽略它。结果中最小的差值为16字节,直到我们申请16字节时,这个差值变成了24,后面也有类似的规律,那么我们可以认为申请所得的内存结构是如下这样的:
从图中不难看出,当我们要分配一段内存时,所得的内存地址和上一次的尾地址至少要相距8个字节(在DEBUG版中还要更多),那么我们可以猜想,这8个字节中应该记录着与这段所分配的内存有关的信息。观察这8个节内的内容,得到结果如下:
图中右边为每次分配所得的地址之前8个字节的内容的16进制表示,从图中红线所表示可以看到,这8个字节中的第一个字节乘以8即得到相临两次分配时的距离,经过试验一次性分配更大的长度可知,第二个字节也是这个意义,并且代表高8位,也就说前面空的这8个字节中的前两个字节记录了一次分配内存的长度信息,后面的六个字节可能与空闲内存链表的信息有关,在翻译内存时用来提供必要的信息。这就解答了前面提出的问题,原来C/C++在分配内存时已经记录了足够充分的信息用于回收内存,只不过我们平常不关心它罢了。