栈和堆的区别以及栈数组和堆数组的区别
这里写得很简洁,实际上堆的机制比较复杂,我详细地学习了 Windows 下的堆管理机制,如果对这部分感兴趣的话,可以参考我的另一篇文章:https://www.cnblogs.com/XiuzhuKirakira/p/16986744.html
栈和堆的区别
1)生命周期
栈:生命周期在被调用函数内,不调用函数就不生成栈
堆:生命周期由程序员决定,new出现,delete消亡
2)初始化
栈在内存中被初始化为: 0xcccccccc
堆在内存中被初始化为: 0xcdcdcdcd (也没有这么绝对,此处的堆特指使用 new 申请的内存块,且为 Debug 状态)
对于堆(heap),若 int *v1 = new int[2]; 则在内存空间中应是:
0xFDFDFDFD
0xCDCDCDCD
0xCDCDCDCD
0xFDFDFDFD
注意:
一个进程可以有若干栈[][][][],但是只有一个堆[ [] [] ] 即进程默认堆
栈内存 | 堆内存 | |
---|---|---|
典型用例 | 函数局部数组 | 动态增长的链表等数据结构 |
申请方式 | 在程序中直接声明即可,如char buffer[8] | 需要用函数申请,通过返回的指针使用。如p=malloc(8) |
释放方式 | 函数返回时,由系统自动回收 | 需要把指针传给专用的释放函数,如free |
初始化 | 0xCCCCCCCC | 0xFDFDFDFD 0xCDCDCDCD 0xFDFDFDFD |
管理方式 所处位置 | 申请后直接使用,申请与释放由系统自动完成,最后达到栈区平衡 | 需要程序员处理申请与释放 变化范围很大,0x0012XXXX |
增长方向 | 由内存高址向低址增加 | 由内存低址向高址排列 |
生命周期 | 生命周期在被调用的函数内,不调用函数就不生成栈 | 生命周期由程序员决定,new/malloc出现,delete/free消亡 |
栈数组与堆数组 | 1)栈数组内存在栈上:int v1[] = {1, 20, 3, -1}; 2)栈数组名不能被修改 | 1)堆数组需要从栈数组上一个内存去访问堆上的内存: int *v2 = new int[4]; //栈上4/8个字节,堆上16个字节 2)堆数组名可以被修改 |
栈数组和堆数组的区别
1)栈数组的内存在栈上,而堆数组需要从栈数组上的一个内存去访问堆上的内存
如下:
int v1[] = { 1,20,3,-1 };//16个字节
int *v2 = new int[4];//栈上4/8个字节,堆上16个字节
2)地址分布
低地址
栈 ebp esp
堆
代码段 .txt
数据段 .data .rdata
高地址
3)栈数组数组名不能被修改,堆数组名可以
int v1[] = { 1,20,3,-1 };//栈上16个字节
int *v2 = new int[4];//栈上4/8个字节(在栈上只是一个指针,在 x86 系统下指针字节为 4,在 x64 系统下指针字节为 8),堆上16个字节
int i = 0;
for (i = 0; i < 4; i++)
{
*v2 = v1[i];
v2++;//改变不会报错,但如果后面不改回去,delete程序就会崩溃
}
v2--;
v2--;
v2--;
v2--;
if (v2 != NULL)
{
delete v2;
}
4)对于栈,堆数组使用sizeof,结果不一样
cout << sizeof(v1) << endl;//输出16
cout << sizeof(v2);//输出4或8
5)堆相对于栈而言,更利于用户的友好界面
_tprintf(_T("How Many\r\n"));
int count = 0;
int*v3;
_tscanf_s(_T("%d"), &count);
if (count == 0 || count > 10)
{
return;
}
else
{
v3 = new int[count];
}