《高质量程序设计指南》之内存管理读书笔记

来源:互联网 发布:网络空间搜索引擎实现 编辑:程序博客网 时间:2024/06/05 06:44

《高质量程序设计指南》第16章内存管理

其中,后四小节:16.12用对象模拟指针,16.13 泛型指针auto_ptr16.14带有引用计数的智能指针,16.15 智能指针作为容器元素个人感觉比较难,有待以后深入学习。

前面几小节说的内容很重要,需要时常温故。

下面是一点笔记内容:

一内存的三种分配方式

1 从静态存储区域分配。特点:在程序编译的时候就已经分配好了,这些内存在程序的整个运行期间都存在,如全局变量、static变量等。

2 在堆栈上分配。特点:在函数执行期间,函数内局部变量(包括形参)的存储单元都创建在堆栈上,函数结束时,这些存储单元自动释放(堆栈清退)。注意:分配的内存容量有限,可能出现堆栈溢出。

3 从堆(heap)或自由存储空间上分配,也称动态内存分配。特点:在程序运行期间,调用mallocnew申请任意数量的内存,程序员自己掌握释放内存的恰当时机(使用freedelete)。

 

一般原则:如果使用堆栈存储和静态存储就能满足应用要求,就不要使用动态存储。

原因:在堆上动态分配内存需要很可观的额外开销。P287详述原因。

 

二常见的内存错误及其对策

1 内存分配未成功,却使用了它。

常用的解决办法是:在使用内存之前检查指针是否为NULL。如果指针P时函数的参数,那么在函数的入口处用assert(p!=NULL)进行检查以避免输入非法参数。如果用malloc()new来申请内存,应该用if(p==NULL)if(p!=NULL)或者捕获异常来进行错误处理。

2 内存分配虽然成功,但是尚未初始化就使用它。

犯这种错误有两种原因:1没有初始化的意识;2 误以为分配好的内存会自动初始化为全0,导致引用初值错误。

解决办法:我们应该在潜意识里认为全局对象或静态对象及其数组没有初值,无论用何种方式创建数组,都别忘了赋初值,即便是赋零值也不可省略,不要嫌麻烦。

3 内存分配成功且已经初始化,但操作越过了内存的边界。

例如:在使用数组时经常发生下标“多1”或者“少1”的操作。特别是在for循环语句中,循环次数很容易高潮,导致数组元素访问越界。

4 忘记了释放内存或者只释放了部分内存,因此造成内存泄露

5 释放了内存却还在继续使用它。

p288详述

规则1 mallocnew申请内存之后,应立即检查指针值是否为NULL或者进行异常处理,以防止使用值为NULL的指针。

规则2 不要忘记初始化指针、数组和动态内存,防止将未初始化的内存作为右值使用。

规则3 避免数组或指针的下标越界,特别当心“多1”或者“少1”的操作。

规则4 动态内存的申请和释放必须配对,防止内存泄露。

规则5 freedelete释放了内存之后,立即将指针设置为NULL,防止“野指针”。

 

freedelete把指针怎么啦!!!

freedelete只是把指针所指向的内存给释放掉,并没有把指针本身删除掉。如果不把这个指针设置为NULL,这个指针就变成了“野指针”。所以再次强调一定不要忘记初始化指针变量为NULL或者有效地址。

 

四、杜绝“野指针”

“野指针”不是NULL指针,而是指向“非法”内存的指针。程序员一般不会错用NULL指针,因为用if语句很容易判断。但是“野指针”是很危险的,if语句对它可能不起作用。

“野指针”的成因主要有以下3种:

1 没有初始化指针变量。任何指针变量刚被创建时不会自动成为NULL指针,它的默认值是随机的,它会乱指一气。所以,指针变量在创建的同时应当初始化,要么将指针设置为NULL,要么让它指向有效的内存。

2 指针Pfree()delete之后,没有置位NULL,让人误以为p仍然是一个有效的指针。

3 指针操作超越了变量的作用范围,这种情况让人防不胜防。

 

malloc / freenew / delete的不同

1 malloc()free()C/C++语言的标准库函数,new/deleteC++的运算符,它们都可用于申请和释放动态内存。

2 由于malloc()free()是库函数而不是运算符,不在编译器控制权限之内,不能把调用构造函数和析构函数的任务强加给它们。

3 不要企图用malloc()free()来完成动态对象的内存管理,应该用new/delete。由于内部数据类型的“对象”没有构造和析构过程,对他们而言new/deletemalloc()/free()是等价的。

 

malloc / free的使用要点

函数malloc()的原型如下:

void *malloc(size_t size);

malloc()申请一块长度为length的整型数组的内存,程序如下:

int *p = (int *)malloc(sizeof(int) * length);

我们应当把注意力集中在两个要素上:“类型转换”和“sizeof”。

1 malloc()函数返回值的类型是void *,所以在调用malloc时要显式地进行类型转换,将void *转换成所需的指针类型;

2 malloc()函数本身并不识别要申请的内存是什么类型,它只关心内存的总字节数。在malloc()函数的“()”中使用sizeof运算符是良好的风格。

0 0