Part1:
程序的内存分配
1、 栈区【函数的参数、局部变量】
由编译器自动分配和释放,存放函数的参数、局部变量等
内存分配是来连续分配的,当申请变量时,编译器自动在其尾部分配一块内存
2、 堆区 [malloc free / new delete]
由程序员分配和释放[malloc free / new delete](需要指明所需内存大小),如果忘记或是没有释放内存,程序结束时可能由操作系统释放
内存分配是不连续的,类似链表,它们是不同区域的内存块通过指针链接在一起的
3、 全局区/静态区【全局变量/静态变量】
存放全局变量和静态变量,初始化的全局变量和初始化的静态变量放在一个区域;
未初始化的全局变量和未初始化的静态变量放在一起
程序结束后,系统释放
4、 文字常量区【字符串常量】
存放字符串常量
程序结束后,由系统释放
5、 程序区【二进制代码】
存放程序的二进制代码
Demo:
char *p=(char *)malloc(20); //p在栈区,分配的20字节在堆区
char s=”123”; //s在栈区,“123”在文字常量区
经典的例子:
Main.cpp
Int a=0; //全局初始化区
char p1; //全局未初始化区
main()
{
Int b; //栈区
Char s[]=”abc”;//栈区
Char p2; //栈区
Char p3=”123456”; //p3栈区 123456\0常量区
static int c=0; //全局(静态)初始化区
p1=(char)malloc(10); //堆区
p2=(char)malloc(20); //堆区
}
Part2:栈区和堆区的区别
数据结构堆栈:
栈:一种后进先出性质的数据结构
就是说后存放的先取,先存放的后取。
堆:一颗倒过来的树
堆是一个经过排序的树形结构,每个结点都有一个值。通常我们所说的堆的数据结构是指二叉堆。堆的特点是根结点最小(大),且根结点的两个子树也是一个堆。堆的存取是随意的,我们可以直接取出我们想要的结点。
内存分配中的堆区和栈区
关于内存分配,一般情况下程序存放在Rom和Flash,运行时需要拷贝到内存中执行,内存会分别存储不同的信息。
申请方式和回收方式不同
栈(stack)是系统自动分配空间的,例如我们定义一个char a; 系统会自动在栈上为其开辟空间。由于栈上的空间是自动分配自动回收的,所以栈上的数据生存周期只是在函数的运行过程中,运行过后就释放掉,不可以再访问。
堆(heap)是程序员根据需要自己申请的空间,例如malloc(10);开辟十字节的空间。而堆上的数据只要程序员不释放空间就一直可以访问到,不过唯一的缺点是一旦忘记释放会造成内存泄露。
申请后系统的响应
栈:只要栈的剩余空间大于所申请的空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请,会遍历链表,寻找一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。
另外对于大多数系统,会将这块内存空间的首地址记录本次分配的大小。这样,代码中delete语句才能正确的释放本地内存空间
另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
也就是说,堆会在申请后还要做一些后续的工作,这就会引出申请效率的问题。
申请效率的比较:
栈:由系统自动分配,速度较快,但是程序员无法控制
堆:由new分配内存,一般速度较慢,而且容易产生内存碎片,不过用起来最方便。
申请大小的限制:
栈区:
在window下栈是向低地址扩展的,是一块连续的内存区域。栈顶地址和栈的最大容量是预设的(在windows下是2M(可能值))。当所需内存超过栈中剩余内存时,将会提示overflow。由此可见,栈的内存一般较小。
堆区:
堆是向高地址扩展的,不连续分配的空间(遍历空闲链表从低地址到高地址)。这是由于系统是用链表来存储的空闲内存地址,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆大小受限于计算机中有效虚拟内存。因此,堆获得的空间比较灵活,也比较大。
堆和栈中的存储内容:
栈:
由于栈的大小有限,所以用子函数还是有物理意义的,而不仅仅是逻辑意义。
在函数调用时,第一个进栈的是主函数中函数调用后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数的局部变量。注意静态变量是不入栈的。
函数结束时,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。
存取效率的比较:
Char s1[]=”aaaaaaaaaaaaaaaaaaaaaaaaaa”;
Char *s2=”bbbbbbbbbbbbbbbbbbbbbbb”;
aaaaaaaaaaaaaaaaaaaaaaaaaa是在运行时赋值的,放在栈中;
bbbbbbbbbbbbbbbbbbbbbbb是在编译时就确定的,放在堆中;
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
1、 分配方式
栈区由编译器自动分配与释放
堆区需要自己指定大小来进行分配与释放
2、 效率
栈区:
栈中只要剩余空间大,编译器将自动分配内存,所以效率比较高,但是程序员无法控制
堆区:
系统会维护一个空闲的堆内存地址的链表。当在堆中申请一块内存时,系统会遍历此表,然后找到一个大于所申请空间的内存节点,将此内存分配给程序并将其从空闲链表中删除。
一般所分配的内存的首地址会记录内存的大小,方便之后delete时释放这些内存。同时,若找到的内存大于所需内存,系统会将空闲部分加入到空闲链表中。由此可见,堆内存分配较慢,容易产生内存碎片。
3、 大小限制
栈区:
在window下栈是向低地址扩展的,栈顶地址和栈的最大容量是预设的(在windows下是2M(可能值))。当所需内存超过栈中剩余内存时,将会提示overflow。由此可见,栈的内存一般较小。
堆区:
堆是向高地址扩展的,不连续分配的空间(遍历空闲链表从低地址到高地址)。堆大小受限于计算机中有效虚拟内存。因此,堆获得的空间比较灵活,也比较大。
4、 存储内容
栈区:
在函数调用时,第一个进栈的是主函数中第一条可执行命令的地址,然后是函数的参数,其次是函数的局部变量。
函数结束时,局部变量先出栈,然后是参数,最后是可执行命令的地址。
程序由此地址继续执行。
堆区:
堆的头部记录内存的大小,方便之后释放。堆内其他内容由程序员决定