程序需要用到的内存可以分为四大类:
- 编译程序的代码区内存
- 存储全局变量的全局区内存
- 堆,动态分配的变量所需的内存就是从堆中分配的。
- 栈,参数和局部变量就是从栈中分配的。
堆
堆是一个大的“内存池”,供动态分配内存之用。在C++中,用new 操作符分配的内存就是从堆中分配的。
int *pValue = new int; // pValue is assigned 4 bytes from the heap
int *pArray = new int[10]; // pArray is assigned 40 bytes from the heap
我们并不确定将在堆中分配那一块地址,所以new返回的是一个指针。我们连续两次用new分配内存,这两块内存并不一定是连续的。
int *pValue1 = new int;
int *pValue2 = new int;
// pValue1 and pValue2 may not have sequential addresses
当动态分配的变量删除后,所占用的内存会归还给堆。
堆的利弊:
- 内存分配出去后一直被占用,知道归还后才能被其他变量申请。
- 动态分配的内存必须用指针才能访问
- 以为堆的容量很大,所以大的数组、结构体或者类一般应该分配在堆上。
上面说到,参数和局部变量从栈中分配内存。参数和局部变量属于函数的一部分,所以我们看看函数调用的时候栈上相应的操作。
- 将当前函数的指令的地址存储在栈中,当被函数调用结束后cpu根据这个地址返回执行之前的操作。
- 在栈中开辟空间,供被调函数返回值所需。
- cpu跳到被调用函数中执行
- 被调用函数中的局部变量压入栈中。
- 被调用函数返回值的一份拷贝存储在上边2中的空间中。
- 栈顶指针回退,栈上的参数和局部变量将销毁
- 函数返回值赋值给当前变量
- cpu从栈中取出之前存入的指令地址重新执行。
当局部变量分配过大或者函数调用深度过大时就会发生栈溢出,导致程序崩溃。例如:
int main()
{
int nStack[100000000];
return 0;
}
栈的利弊:
- 从栈中分配的变量会随着栈销毁而销毁
- 在编译的时候,从栈中分配的内存是连续的,所以可以通过变量名直接访问。
- 由于栈空间相对较小,所以大的数组、结构体或者类就不要从栈中分配了。