前面已经探讨了栈结构 这里对堆也跟踪一下:
int a;
int* pb = new int;
cout<<"a is from stack, b is from heap"<<endl;
cout<<"(int)a= "<<a<<endl;
cout<<"(int)b= "<<*pb<<endl;
cout<<"(hex)a= "<<hex<<a<<endl;
cout<<"(hex)b= "<<hex<<*pb<<endl;
char* pa = (char*)&a;
cout<<"(char)a= "<<pa<<endl;
cout<<"(char)b= "<<(char*)pb<<endl;
delete pb;
return 0;
} ![]()
int*a = new int;
int*b = new int;
![]()
![]()
堆的分配规则和栈不一样 堆的地址是从小到大分配的 并且连续分配的两个内存块的起始地址是肯定不同的。
写在前面: 关于内存所谓的空闲区值:
经常写代码忘了初始化的人可能会注意到,每次定义一个整形变量没有初始化直接使用 往往每次都得到一个很大的负数 不是说没有分配的内存都是随机的么?看这个示例:
int main()
{int a;
int* pb = new int;
cout<<"a is from stack, b is from heap"<<endl;
cout<<"(int)a= "<<a<<endl;
cout<<"(int)b= "<<*pb<<endl;
cout<<"(hex)a= "<<hex<<a<<endl;
cout<<"(hex)b= "<<hex<<*pb<<endl;
char* pa = (char*)&a;
cout<<"(char)a= "<<pa<<endl;
cout<<"(char)b= "<<(char*)pb<<endl;
delete pb;
return 0;
}
vc++6.0输出结果:

我相信这里面出现的这两个字大家都很熟悉了,就连上面的两个负整数应该都是异常眼熟吧 接下来 揭开谜底:
对于栈和堆的空闲区(未被覆盖前) 是会被默认初始化的, 栈内存初始化为CC 堆初始化为CD的 初看起来了解这个意义不大 但这是下面所说的一些东西的基础。
示例2:
int main()
{int*a = new int;
int*b = new int;
int*c = new int;
delete a;
delete b;
delete c;
return 0;
}
delete a;
delete b;
delete c;
return 0;
}
调试 得到
a = 00382a48
b = 00382a90
c = 00382ad8
内存跟踪:

这里面包含了很多信息
1.b指针指向的地址处(00382a90) 被默认初始化为CD
2.空闲区两头是有四个字节的界定符的 这里是FD FD FD FD
3.继续往上看 00382a70处 有一个地址00382a28
4.下一个地址 00382a74处 有一个地址00383ab8
在这里我们不妨假设这两个地址就是已经被分配的内存的下一块和上一块
接下来 我们对00382a28进行跟踪:

在该地址下面不远处 我们找到了00382a48 也就是指针a指向的地址 那么到了这里我们可以看出:
堆中每一个内存块是被连接到一个链表上的 并且每一个空闲区都被封在一个结构体中
这个结构体至少应该包含
该数据块的前驱和后继
该数据块大小
数据块的界定符
数据块本身
在这里我们可以看出 放在数据块前面的数据区大小=00382a48-00382a28=32个字节
同时对其中另一部分数据进行分析 初步可以猜测:
00382a38 分配的元素类型所占字节数 这里是sizeof(int)
00382a3c 分配该类型元素的个数
到这里也可以明白为什么动态分配一个数组int* p=new int[5] 只需要delete []p 就可以了 因为结构体保存了元素个数
随便再说一下 对于CD CC只针对于还未使用过的内存 比如像栈这种动态收缩的结构 同一个内存不同时刻会存放不同的变量 在使用后回收时 只改变栈顶指针 数据是还在的 所以不是CC