zoukankan      html  css  js  c++  java
  • 内存管理

    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)指针操作超越了变量的作用范围。

    例如:

    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 是一段连续的存储区 

  • 相关阅读:
    【LeetCode】147. Insertion Sort List
    【LeetCode】64. Minimum Path Sum
    【LeetCode】7. Reverse Integer
    【LeetCode】107. Binary Tree Level Order Traversal II (2 solutions)
    【LeetCode】114. Distinct Subsequences
    【LeetCode】35. Search Insert Position (2 solutions)
    为什么easyui的datagrid里getSelections还有getChecked只能获取一行值呢?
    IE兼容模式下 SCRIPT1028: 缺少标识符、字符串或数字
    修改easyui的easyloader的默认css目录路径
    如何在Visual Studio的查找功能中使用正则表达式?
  • 原文地址:https://www.cnblogs.com/sky0917/p/3529783.html
Copyright © 2011-2022 走看看