c++内存管理内存分配实例源码(1),c++内存管理(1)。
7.1 内存分配方式
一个C、C++程序编译时内存分为5大存储区:堆区、栈区、全局区、文字常量区、程序代码区。
(1) 在静态存储区域分配
控制者:编译器
分配时间:在程序编译的时候分配内存
释放时间:在程序的整个运行期间都存在,程序结束后由OS释放
内容:全局变量,static变量
特点:
0、速度快,不易出错。
1、初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和静态变量在另一块区域
2、定义后,变量的值可以改变
(2) 在栈上创建
控制者:由编译器自动分配释放
分配时间:在程序运行(执行函数)的时候分配内存
释放时间:在函数执行结束时,释放存储单元自动被释放。
举例: 局部变量,函数参数
特点:
1、栈内存分配运算 内置于处理器的指令集中,分配效率很高,但是分配的内存容量有限。
2、定义后,变量的值可以改变
(3) 从堆上分配
控制者:程序员一般由程序员分配和释放,。
分配时间:在程序运行(遇见new或malloc)的时候分配内存。
释放时间:程序员自己决定,若程序员不释放,程序结束时可能由OS回收,但是程序运行期间不释放的内存属于内存泄露。
举例:使用new 和 malloc申请的空间
特点:
0、频繁地分配和释放不同大小的堆空间将会产生堆内碎块
1、程序员使用malloc 或new 申请任意多少的内存,自己负责在何时用free 或delete 释放内存,否则会造成内存泄露。
2、定义后,变量的值可以改变
(4) 文字常量区
控制着:编译器
分配时间:在程序编译的时候分配内存
释放时间:程序结束后由系统释放
举例:常量字符串
特点:定义后,变量的值不可以 改变,只读的
(5) 程序代码区
内容:存放函数体的二进制代码
举例:
1、存储位置
- 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); 分配 得来得10和20字节的区域就在堆区。
- strcpy(p1, ”123456″); 123456\0放在常量区,编译器可能会将它与p3所指向的”123456″优化成一个地方。
- }
2、内容是否改变
- main()
- {
- static char b = ’a'; //静态初始化区
- b = ’c'; //定义后,值可以修改
- static char a[] = ”hello”; //静态初始化区
- a[0] = ’X'; //定义后,值可以修改
- ——-
- char *p = ”world”; // 注意p 指向常量字符串,值不可以改变
- p[0] = ’X'; //错误,该指针指向的内容不能改变,编译器不能发现该错误,
- strcpy(p,”123″); //错误,该指针的指向不能改变, 编译器不能发现该错误
- }
注意:定义并初始化指针的值 是 存在常量区的,指针指向的值 以及 指针的指向 都是不能改变的
7.2 常见的内存错误及其对策
(1) 内存分配未成功,却使用了它
解决办法:在使用内存之前检查指针是否为NULL
举例:如果指针p 是函数的参数,那么在函数的入口处用assert(p!=NULL)进行检查。
如果是用malloc或new来申请内存,应该用if(p==NULL)或if(p!=NULL)进行防错处理。
(2) 内存分配虽然成功,但是尚未初始化就引用它
解决方法:以无论用何种方式创建数组,都别忘了赋初值,不要使用默认值。即便是赋零值也不可省略,不要嫌麻烦。
(3) 内存分配成功并且已经初始化,但操作越过了内存的边界
(4) 忘记了释放内存,造成内存泄露
解决方法:动态内存的申请与释放必须配对,程序中malloc 与free 的使用次数一定要相同,否则肯定有错误(new/delete 同理)。
或者,使用两个变量,分别记录申请空间的数目 和 释放空间的数目。如果二者相等,表示无内存泄露
(5) 释放了内存却继续使用它
有三种情况:
(1) 程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,
解决方法:此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。
(2) 函数的return 语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”
(3) 使用free 或delete 释放了内存后,没有将指针设置为NULL。导致产生“野指针”。
解决方法:释放内存后,主动的把指针赋NULL
7.7 杜绝“野指针”
“野指针”的成因主要有两种:
(1) 指针变量没有被初始化。
注意:任何指针变量刚被创建时不会自动成为NULL 指针,它的缺省值是随机的,它会乱指一气
解决方法:指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
- char *p = NULL;
- char *str = (char *) malloc(100);
(2) 指针p 被free 或者delete 之后,没有置为NULL,让人误以为p 是个合法的指针
解决方法: 释放指针后直接赋NULL
(3) 指针操作超越了变量的作用范围。这种情况让人防不胜防
- class A
- {
- public:
- void Func(void){ cout << “Func of class A” << endl; }
- };
- void Test(void)
- {
- A *p;
- {
- A a;
- p = &a; // 注意 a 的生命期
- }
- p->Func(); // p 是“野指针”
- }
函数 Test 在执行语句p->Func()时,对象a 已经消失,而p 是指向a 的,所以p 就成了“野指针”。但奇怪的是我运行这个程序时居然没有出错,这可能与编译器有关。