VC6.Debug编译方式下堆的链式管理

##0x0 相关知识
堆空间是程序运行时由程序员自己申请的空间,该空间需要程序员自己释放。
在C++语言中,使用new申请堆空间,使用delete释放堆空间。在C语言中,使用malloc和free函数对堆空间的申请和释放。
而与之对应的栈空间,栈空间是由系统进行维护的空间,局部变量和函数的参数使用的都是栈空间,栈空间的分配和回收都是由系统自动进行维护。
PS:在main函数中定义一个变量int a,那么系统就会自动给它分配栈内存,即局部变量使用栈空间;
printf(“This is test: %d”,a),那么此时打印a的值时候会使用寄存器,作为参数压入栈中,即传参使用栈空间;
##0x1 测试程序

#include <iostream>
int main()
{
	//定义3个整型的指针变量
	int *p = NULL;			//32位的整型变量指针
	__int64 *q = NULL; 		//64位整型变量指针
	int *m = NULL;			//32位
	
	//使用new分配一个整型的内存空间
	//用指针变量p指向该内存空间
	p = new int;
	if(p == NULL)	//使用之前一定要判断是否成功分配内存
	{
		return -1;
	}
	//为指针变量p指向的内存空间赋值
	*p = 0x11223344;
	
	//q和m操作同p
	q = new __int64;
	if( q == NULL)
	{
		return -1;
	}
	*q = 0x1122334455667788;
	
	m = new int;
	if( m == NULL)
	{
		return -1;
	}
	*m = 0x11223344;
	
	//释放3个变量指向的地址空间
	delete q;
	q = NULL;
	delete m;
	m = NULL;
	delete p;
	p = NULL;
	return 0;
}

PS:iostream和iostream.h的区别

iostream.h与iostream是不同的。
#include<iostream.h>是在旧的标准C++中使用。在新标准中,用#include。iostream的意思是输入输出流。#include是标准的C++头文件,任何符合标准的C++开发环境都有这个头文件。还要注意的是:在VC编程时要添加:
using namespace std;
其原因是:后缀为.h的头文件C++标准已经明确提出不支持了,早些的实现将标准库功能定义在全局空间里,声明在带.h后缀的头文件里,C++标准为了和C区别开,也为了正确使用命名空间,规定头文件不使用后缀.h。因此,当使用<iostream.h>时,相当于在C中调用库函数,使用的是全局命名空间,也就是早期的C++实现;当使用的时候,该头文件没有定义全局命名空间,必须使用namespace std;这样才能正确使用cout。 —百度百科

##0x2 调试分析

(1) 3个指针变量分配的内存,分别是19ff3c,19ff38,19ff34。从这里看到,主函数中先定义的变量的地址(局部变量使用的是栈地址)要大于后定义的变量的地址,并且变量的地址按照定义顺序依次紧挨。三个变量值均为0xcccccccc(VC6debug编译方式下默认对局部变量初始化的值)。
局部变量初始化
(2)执行到p=new int;3个变量都被复制为NULL。执行完p=new int;后,p申请到的堆内存为0x00783a58,其存储的值为0xCDCDCDCD(VC6的Debug编译方式下,未进行赋值的堆空间的值为0xCDCDCDCD)。
申请到的堆内存
(3)调试到此处,为指针p指向的堆内存空间赋值为0x11223344。此时一个整型指针赋值完毕。
理一下地址关系,此时指针变量 p的地址:0x0019ff3c,p指向的地址:0x00783a58,p指向的地址中的值:0x11223344。
为指针指向的内存空间赋值
PS:指针就是地址的说法是不严谨的,准确来说,指针是有类型的地址。”*”操作需要根据指针的类型来进行取值的。对于一个指针,应该了解4个方面:指针的类型、指针的地址、指针指向的地址、指针指向地址的值。

(4)执行到此处,可以看到三个指针分别指向的空间:0x00783a58, 0x00783aa0, 0x00783ae8。
将p指向的地址减0x20字节,即0x00783a58 – 0x20 = 0x00783a38,
将q指向的地址减0x20字节,即0x00783aa0 – 0x20 = 0x00783a80,
将m指向的地址减0x20字节,即0x00783ae8 – 0x20 = 0x00783ac8,在内存窗口观察。
0x00783a38地址处的值为:0x007839D0,0x00783a80。
0x00783a80地址处的值为:0x00783a38,0x00783ac8
0x00783ac8地址处的值为:0x00783a80,0x00000000
3个堆内存空间布局
整理一下上面的地址,就可以看出来其实就是一个双向链表,即new申请的堆空间是通过双向链表进行管理的。最后一个结点的0x00000000表示链表的结尾。
堆的链式管理
(5)链表是链式管理,堆空间的首地址是管理双向链表的指针,以第一个结点(堆空间)为例:
①在首地址偏移0x10的位置记录了堆空间的大小,第一个堆空间的首地址是0x00783a38,偏移0x10的位置是0x00783a48,此地址保存的值是0x4。其余几个new申请的空间的大小通过这种方式也可以找到。
②在堆空间偏移0x18的位置记录堆的一个序号,程序通过new申请的第一块堆空间的序号为0x40,第二块为0x41,第三块为0x42。

(6)每个数值的前后(对指针赋的值)都有4个”FDFDFDFD”,前后的FD是用来在调试时检测溢出的,当为指向整型地址的p变量赋值超过4个字节时,就会覆盖后面的FD,当调试程序时,通过查看FD的值,就可以观察到赋值溢出了。
检测溢出填充标志
(7)当使用new申请的空间不再使用时,会使用delete释放空间,释放后的堆空间会被赋值为”FEFE”,第1块堆的后继链表指针指向了第3块堆,第3块的前驱链表指针指向了第1块堆。
在这里插入图片描述
全部释放后的堆空间
VC默认提供两种编译方式,分别为Debug和Release。上面的堆管理为Debug编译方式,Release编译方式是与此不同的。之后再分析吧。。。

学长的博客:https://blog.roachs.cn/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值