栈:由操作系统自动分配释放,用于存放函数的参数值、局部变量等。它的的操作方式类似于数据结构中的栈,都是后进先出。例如:
int main()
{
int a;//栈
char arr[] = "abcd";//栈
char *p;//栈
}
函数中定义的局部变量按照先后定义的顺序依次压入栈中,栈的内存伸展方向是向下的,即由高到低,因此后定义的变量地址要低于先定义的变量地址。栈中存储的数据的生命周期随着函数的执行完成而结束。
堆:由程序员分配释放,若程序员不释放,程序结束是由OS进行回收,它的分配方式类似于链表。
int main()
{
//C语言中使用malloc函数申请
char* p1 = (char*)malloc(5);
//使用free函数释放
free(p1);
//C++中用new运算符申请空间
char* p2 = new char[10];
//使用delete运算符释放
delete[] p2;
}
p1和p2所指的的空间都是存在于堆的。堆的内存地址生长方向是向上的,即由低到高。但值得注意的是:后申请的内存空间不一定在先申请的内存空间的后面,即p2指向的地址并不一定大于p1所指向的地址。原因在于,先申请的内存空间一旦被释放,后申请的内存空间则会利用先被释放的内存,从而导致先后分配的内存空间在地址上不存在先后关系。堆中存储的数据若为释放,其生命周期与程序的生命周期相等。
另外补充一下:堆上内存空间的分配过程,首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个大于所申请空间的堆节点,然后将该节点从空闲节点链表中删除,并将该节点的空间分配给程序。对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,通过这种方式,代码中的delete语句才能正确地释放本内存空间。由于找到的堆节点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空间链表。
堆和栈的区别
1.管理方式不同。栈由操作系统自动分配释放,不需要我们手动控制;堆的申请和释放工作由程序员控制,因此容易产生内存泄漏。
2.空间大小不同。栈的大小一般只有8~10M,而堆有几个G。
3.生长方向不同。栈的生长方向向下,内存的地址由高到低,堆的生长方向向上,内存的地址由低到高。
4.分配方式不同。堆时动态分配的;栈有两种分配方式:静态分配和动态分配。静态分配是由操作系统完成,比如局部变量的分配。动态分配由malloc函数进行分配,但是栈的动态分配和堆是不同的,栈的动态分配是由操作系统进行释放,不需要我们手动释放。
5.分配效率不同。栈由操作系统自动分配,会在硬件层级对栈提供支持。分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是由C/C++提供的库函数或运算符来完成申请与管理,实现机制比较复杂,频繁的内存申请容易产生内存碎片。显然,堆的效率比栈低得多。
由此可见,堆和栈相比,由于大量malloc()/free()或new/delete的使用,容易造成大量的内存碎片。栈相比于堆,在程序中应用较为广泛,最常见的函数调用过程由栈来实现,函数返回地址、实参和局部变量都采用栈的方式存放。虽然栈有众多的好处,但是和堆相比不是那么灵活,有时候分配大量的内存空间任然需要用堆。总之无论是堆还是栈,在内存使用时都要防止非法越界。