一、内存分配方式
-
在 C++ 中,内存分成 5 个区:堆、栈、自由存储区、全局/静态存储区和常量存储区
-
1、栈
执行函数时,局部变量
的存储单元都可以在栈
上创建,函数执行结束时这些存储单元被自动释放。
栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 -
2、堆
由new分配的内存块
,编译器不去管它们的释放,由应用程序去控制;
一般一个 new 就要对应一个 delete。
如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。 -
3、自由存储区
由 malloc(全称 memory allocation)等分配的内存块,它和堆是十分相似的,不过它是用 free 来结束自己的生命的。 -
4、全局/静态存储区
全局变量和静态变量被分配到同一块内存中 -
5、常量存储区
这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改。
二、区分堆、栈
void f() {int* p = new int[8];}
如上代码就包含了堆与栈,先 new 出来一块堆内存,指针 p 指向一块栈内存。
表示:在栈内存中存放了一个指向一块堆内存的指针 p。
程序经过编译、链接、生成执行程序后,堆和栈的起始地址就已经确定了(具体说,是通过“连接程序”),在一个具有反向增长的栈的 CPU 上,数据空间可表示如下:
在内存中,“堆”和“栈”共用全部的自由空间,只不过各自的起始地址和增长方向不同,它们之间并没有一个固定的界限,如果在运行时,“堆”和 “栈”增长到发生了相互覆盖时,称为“栈堆冲突”,系统肯定垮台。由于开销方面的原因,各种编译在实现中都没有考虑解决这个问题,只有靠设计者自己解决,比如增加内存等。
三、控制内存分配
- C++ 的一个常见问题是内存分配,即对 new 和 delete 操作符的失控。
1、 内存分配未成功,却使用了它。
新手没有意识到内存分配会不成功。常用解决办法是,在使用内存之前检查指针是否为NULL。
如果指针p是函数的参数,那么在函数的入口处用assert(p!=NULL)进行检查。
如果是用malloc来申请内存,应该用if(p==NULL) 或if(p!=NULL)进行防错处理。
2、内存分配虽然成功,但是尚未初始化就引用它。
犯这种错误主要有两个起因:一是没有初始化的观念;二是误以为内存的缺省初值全为零,导致引用初值错误(例如数组)。
内存的缺省初值究竟是什么并没有统一的标准,尽管有些时候为零值,我们宁可信其无不可信其有。
所以无论用何种方式创建数组,都别忘了赋初值,即便是赋零值也不可省略,不要嫌麻烦。
3、内存分配成功并且已经初始化,但操作越过了内存的边界。
例如在使用数组时经常发生下标“多1”或者“少1”的操作。特别是在for循环语句中,循环次数很容易搞错,导致数组操作越界。
4、忘记了释放内存,造成内存泄露。
含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,你看不到错误。
终有一次程序突然死掉,系统出现提示:内存耗尽。
动态内存的申请与释放必须配对,程序中malloc与free的使用次数一定要相同,否则肯定有错误(new/delete同理)。
5、释放了内存却继续使用它。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于