第七章 高质量C编程规范内存管理

1.内存分配方式

(1)从静态存储区域分配。例如全局变量,static 变量。

(2)在栈上创建。例如局部变量。

(3)从堆上分配,亦称动态内存分配。例如malloc、free以及new、delete。

2.常见的内存错误及其对策

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

常用解决办法是,在使用内存之前检查指针是否为NULL。可以用判断空或者断言检查。

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

不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。

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

(4)忘记了释放内存,造成内存泄露。

(5)释放了内存却继续使用它。

函数的return语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁;使用free 或delete释放了内存后,没有将指针设置NULL。

3.指针数组的区别

(1)分配内存的存储位置不一样。指针在堆区,数组在栈或者静态存储区。

char *p = “world”; // 注意p 指向常量字符串

p[0] = ‘X’; // 编译器不能发现该错误,运行会出错。该语句企图修改常量字符串的内容

(2)用运算符sizeof 可以计算出数组的容量(字节数)。但是sizeof(p)的值却是4。

注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。

4.如果函数的参数是一个指针,不要指望用该指针去申请动态内存。因为该参数是局部变量,会随着函数调用的完成而释放掉。

可以通过下面的两种方法实现:

(1)char *GetMemory3(int num)

{

char *p = (char *)malloc(sizeof(char) * num);

return p;

}

这里强调不要用return语句返回指向“栈内存”的指针,因为该内存在函数结束时自动消亡。如下例:

char p[] = "hello world";

return p; // 编译器将提出警告

然而(1)例的p是指向malloc分配的内存。

(2)void GetMemory2(char **p, int num)

{

*p = (char *)malloc(sizeof(char) * num);

}

5.free和delete只是把p指向的存储区域释放掉,而p本身并没有释放掉。因此在释放掉p所指向的存储区域之后,一定记得将p赋空,防止对未定义区域的操作。

6. 如下代码

void Test(void)

{

A *p;

{

A a;

p = &a; // 注意 a 的生命期

}

p->Func(); // p 是“野指针”

}

a的生命期仅限于内层花括号内。

7. 对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。

既然new/delete的功能完全覆盖了malloc/free,为什么C++不把malloc/free 淘汰出局呢?这是因为C++程序经常要调用C 函数,而C程序只能用malloc/free 管理动态内存。

8.内存耗尽怎么办?

如果在申请动态内存时找不到足够大的内存块,malloc 和new 将返回NULL指针,宣告内存申请失败。通常有三种方式处理“内存耗尽”问题。

(1)判断指针是否为NULL,如果是则马上用return 语句终止本函数。

(2)判断指针是否为NULL,如果是则马上用exit(1)终止整个程序的运行。

(3)为new 和malloc 设置异常处理函数。例如VisualC++可以用_set_new_hander 函数为new 设置用户自己定义的异常处理函数,也可以让malloc 享用与new相同的异常处理函数。

注意:上述(1)(2)方式使用最普遍。如果一个函数内有多处需要申请动态内存,那么方式(1)就显得力不从心(释放内存很麻烦),应该用方式(2)来处理。如果发生“内存耗尽”这样的事情,一般说来应用程序已经无药可救。如果不用exit(1)把坏程序杀死,它可能会害死操作系统。

9. malloc/free 的使用要点

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

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

(3)为什么free 函数不象malloc 函数那样复杂呢?这是因为指针p的类型以及它所指的内存的容量事先都是知道的,语句free(p)能正确地释放内存。如果p是NULL指针,那么free对p无论操作多少次都不会出问题。如果p不是NULL指针,那么free对p连续操作两次就会导致程序运行错误。

10. new/delete 的使用要点

运算符new 使用起来要比函数malloc 简单得多,例如:

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

int *p2 = new int[length];

这是因为new 内置了sizeof、类型转换和类型安全检查功能。对于非内部数据类型的对象而言,new在创建动态对象的同时完成了初始化工作。如果对象有多个构造函数,那么new 的语句也可以有多种形式。例如:

Obj *a = new Obj;

Obj *b = new Obj(1); // 初值为1

如果用new 创建对象数组,那么只能使用对象的无参数构造函数。

在用delete 释放对象数组时,留意不要丢了符号‘[]’。例如

delete []objects; // 正确的用法

delete objects; // 错误的用法

后者相当于delete objects[0],漏掉了另外99 个对象。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值