____________________
| Stack区(栈区)(函数参数,局部变量,数组);自动创建,函数结束时自动释放,速度快,容量小
|____________________
| Static存储区(static变量,全局变量); 程序编译的时候就分配好
|____________________
| ; 通过malloc/new等资源申请函数分配,需要程序员主动释放
| Heep区(堆区) 比较大 ; 速度慢,容量大
|____________________
| 代码段
____________________
//main.cpp
int a = 0; // 全局初始化区
char *p1; // 全局未初始化区
main()
{
static int c =0; // 全局(静态)初始化区
int b; // 栈
char *p2; // 栈
char s[] = "abc"; // s在栈, "abc"+‘\0‘在文字常量区
char *p3 = "123456"; // p3在栈,"123456" + ‘\0‘在常量区
p1 = (char *)malloc(10); // 分配得来得10字节的区域在堆区
p2 = (char *)malloc(20); // 分配得来得20字节的区域在堆区
strcpy(p1, "123456"); // "123456" + ‘\0‘在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}
栈
1. 栈的访问速度快,只需要移动栈顶指针就可以;
2. 函数局部变量在栈里面,栈的空间有限(1MB-2MB),在函数里面声明一个非常大的数组有可能是会出错的;
3. 特别注意递归函数醉倒不要使用大的栈对象,因为随着递归深度的增加,所需要的栈空间也线性增加,有可能出现栈溢出;
4. 栈里面的变量由系统维护,函数退出的时候,这些变量自动释放,这里有变量作用域的概念;
5. 编译器使用栈来存放函数的参数;
6. 栈是线程独立的,每个线程都有自己的栈。
堆
1. 堆的创建和销毁由程序员完成,灵活但容易出错;
2. 堆的创建需要在空闲存储列表上查找匹配块的过程,因此会比较慢。尤其当找不到空闲块适应当前空间申请大小时,会引发存储区整理功能,极具脱慢堆对象的创建。堆对象释放的时候也可能会相邻空闲块的合并等操作;
3. 如果分配了堆对象,却忘记了释放,就会产生内存泄漏。泄露的内存在程序退出时会被操作系统回收,但如果程序长期的执行,并有经常性的内存泄露,这样就会耗尽系统所有的内存资源,包括虚拟内存,这时候系统的任何操作都会变得剧慢无比,甚至直接系统崩溃。因此内存泄露一定要足够重视,并使用专用的工具检测程序;
4. free(ptr);如果不调用ptr=NULL;那么ptr就成为所谓的“悬挂指针”,如果程序某个地方在此使用ptr就会出现非法访问,严重时就导致程序崩溃。因此一个好的习惯是将free(ptr); ptr=NULL;写在一起;
5. 堆是开在内存里面的,但当物理内存不够时,如果还需要生成新的堆对象,通常不会产生运行错误,因为系统会使用虚拟内存来扩展实际的物理内存。
申请堆对象的函数有:malloc和calloc
(1) malloc 在堆区分配指定字节数的存储区,此存储区中的初始值不确定。
(2) calloc 在堆区为指定长度的对象,分配能容纳其指定个数的存储空间。该空间中的每一字节都初始化为0
(3) realloc 更改以前存储区的长度(增加或减少)。当增加长度时,可能需将以前存储区的内容复制到另一个足够大的区域,而新增区域内的初始值则不确定。
buff = calloc(numElements ,sizeOfElement);
等价于
buff = malloc(numElements *sizeOfElement);
memset(buff, 0, numElements *sizeOfElement);
实现malloc的方法有很多种,但一个申请到的存储区结构大概都会包括:1.状态(使用/空闲); 2.长度;3.存储地址;4.下一端存储区地址
buff = malloc(size);
对于用户可见的就是buff这个存储地址,用户可以使用的存储空间是 [buff ... buff+size]。而系统除了给出buff这个地址外,还需要知道这个buff的长度,因为在free(buff)的时候,用户并没有输入长度信息,需要系统自己记录下buff的size。这就需要额外的一些字节。一般这些信息会记录在buff前面的几个地址空间内。因此对于系统来说,buff这个存储空间,对应的实际空间为 [buff-n ... buff+size+n]。buff地址前的部分数据用户不可见,但是是非常重要的,如果程序中有buffer超界的情况,而恰好该buffer后面紧接着另一个buffer,这个超界就是极端可怕的,因为它写花了下一个buffer的长度信息,会引起完全不可知的程序错误。而且这种错误编译器是发现不了,只有一些动态程序分析工具可以run-time的检测到这些错误。所以使用动态buffer时一定要清楚的知道它的大小,以及当前位置。