内存管理

1. 内存的分配方式

内存分配方式有三种:

(1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的
         整个运行期间都存在。例如全局变量,static 变量。
(2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函
         数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集
         中,效率很高,但是分配的内存容量有限。
(3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意
        多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存
        期由我们决定,使用非常灵活,但问题也最多。

 

2. 常见内存错误

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

解决办法:

在使用之前检查指针是否为NULL。如果指针p是函数的参数,那么可以在函数的入口处用assert(p!=NULL)

进行查错。如果是用malloc 或 new 来申请内存,应该用 if(p==NULL) 或 if(p!=NULL)进行防错处理。

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

解决办法:

分配内存之后及时初始化。

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

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

解决办法:

动态内存的申请与释放必须配对,程序中malloc 与free 的使用次数一定要相同,否则肯定有错误(new/delete 同理)。

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

常见的三种情况:

① 程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。

② 函数的return 语句写错了,注意不要返回指向 “栈内存” 的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。

③ 使用free 或delete 释放了内存后,没有将指针设置为NULL。导致产生 “野指针”

 

3. 计算内存容量

用运算符 sizeof 可一计算出数组的容量(字节数)。

       例如:

       char a[] = "hello world";  // sizeof(a) = 12

       char *p = a;                 // sizeof(p) = 4 相当于 sizeof(char*)

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

       例如:

       void Func(char a[100]){

     cout<< sizeof(a) << endl; // 4 字节而不是100 字节
   }

 

4. 指针参数如何传递内存

如果用函数去申请内存可采取下面两种方式:

void getMemory(char **p, int num){            // 指向指针的指针
  *p = (char*)malloc(sizeof(char) * num);
}

调用时: GetMemory(&pstr, 100);

 

char *getMemory2(int num){
  char *p = (char *)malloc(sizeof(char) * num);
  return p;
}

 

下面这种方式将会造成内存泄漏

void GetMemory(char *p, int num){

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

 

不要用return 语句返回指向“栈内存”的指针,因为该内存在函数结束时自动消亡

char *GetString(void){

  char p[] = "hello world";
  return p; // 编译器将提出警告
}

 

5. 防止 “野指针” 的出现

“野指针”不是NULL 指针,是指向“垃圾”内存的指针,形成原因有下面几种:

(1) 指针变量没有被初始化。

任何指针变量刚被创建时不会自动成为NULL 指针,它
的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么
将指针设置为NULL,要么让它指向合法的内存。例如
char *p = NULL;
char *str = (char *) malloc(100);
(2)指针p 被free 或者delete 之后,没有置为NULL,让人误以为p 是个合法的指针。

(3)指针操作超越了变量的作用范围。

例如:

1
2
3
4
5
6
7
8
9
10
11
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 是“野指针”
}

  

6. malloc/free 和 new/delete 的区别

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

对于非内部数据类型 new 在分配内存的同时会调用构造函数,delete 在回收内存时会掉用析构函数,而 malloc/free则没有此功能。

 

7. 如何面对内存耗尽的情况。

通常有三种处理方式:

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

      例如:

内存管理
void Func(void){
    A *a = new A;
   if(a == NULL){
        return;
    }
…
}
内存管理

 

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

       例如:

内存管理
void Func(void){
    A *a = new A;
    if(a == NULL){
         cout << “Memory Exhausted” << endl;
         exit(1);
    }
…
}            
内存管理

 

(3)为new 和malloc 设置异常处理函数。例如Visual C++可以用_set_new_hander 函数为 new 设置用户自己定

      义的异常处理函数,也可以让malloc 享用与new 相同的异常处理函数。

 

7. malloc/free 的使用的注意点

函数 malloc 的原型如下:
    void * malloc(size_t size);
用 malloc 申请一块长度为length 的整数类型的内存,程序如下:
    int *p = (int *) malloc(sizeof(int) * length);

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

(2) malloc 函数本身并不识别要申请的内存是什么类型,它只关心内存的总字节数。我们通常记不住int, float 等数据类

     型的变量的确切字节数。例如int 变量在16 位系统下是2 个字节,在32 位下是4 个字节;而float 变量在16 位系统下是
     4 个字节,在32 位下也是4 个字节。

 

函数 free 的原型如下:
    void free( void * memblock );

对于语句 free(p)如果p 是NULL 指针,那么free 对p 无论操作多少次都不会出问题。

如果p 不是NULL 指针,那么 free 对 p 连续操作两次就会导致程序运行错误。

 

8. new/delete 的使用的注意点

new 内置了sizeof、类型转换和类型安全检查功能。

申请内存时只要

int *p2 = new int[length];

delete p;       // 释放内存

delete []p2;  // p2 是一段连续的存储区 

内存管理

上一篇:== 和 equals 的用法


下一篇:Eclipse中文乱码