C/C++动态内存管理

C/C++动态内存管理

内存分配的方式有3种:

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

C 动态内存管理

C语言使用malloc/calloc/realloc/free进行动态内存管理

malloc函数

void *malloc(size_t size);

功能:允许从空闲内存池中分配连续内存但不初始化。
参数:size参数是一个所需字节数的整数
返回:若分配成功则返回一个指向该内存块的指针,在使用时可根据需要做强制类型转换,否则返回NULL(空指针)

calloc函数

void *calloc(size_t num, size_t size);

功能:同malloc,但作初始化。
参数:num是所需元素的数量,size是每个元素的字节数。
返回:同malloc函数

realloc函数

void *realloc(void *ptr, size_t new_size);

功能:在指针ptr指向的内存基础上扩大或缩小内存
参数:ptr是指向先前通过malloc、calloc、和realloc函数后分配的内存块的指针,new_size是内存块的新尺寸,可能大于或小于原有内存尺寸。
返回:若能调整内存大小,则返回指向调整后内存的指针,否则返回NULL。

realloc函数的使用注意点

  • 当扩展内存时,不会对添加进内存块的字节进行初始化
  • 若不能调整内存则返回NULL,但原有内存中的数据不会发生改变。
  • 若第一个参数为NULL,那么功能等同于malloc函数,若第二个参数为0,那么会释放调用内存块。
  • 在缩小或扩大内存时一般不会对其移动,若无法扩大内存块(内存块后面的字节已经用于其它目的),那么可能会在别的地方分配新的内存块,然后把旧的内存块中的数据复制到新的内存块中,并将旧的内存块删除释放。

free函数

void free(void *ptr);

功能:释放由指针ptr指向的动态分配的内存块

注意

  • 如果ptr是NULL指针,那么free对ptr无论操作多少次都不会有问题。
  • 如果ptr不是NULL指针,那么free对ptr连续操作两次就会导致程序运行错误。
int *p1 = (int*)malloc(sizeof(int)* 4);
int *p2 = (int*)calloc(4, sizeof(int));
int *p3 = (int*)realloc(p2, sizeof(int)* 4);

C++动态内存管理

C++通过new和delete动态管理对象

  • new/delete动态管理对象
  • new[]/delete[]动态管理对象数组

有了malloc/free为什么还要new/delete?

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

对于自定义类型而言,malloc()/free()无法满足动态对象的要求:对象在创建的同时要自动调用构造函数,对象在销毁时要自动调用析构函数。

因此,C++语言需要一个能够完成动态内存分配和初始化工作的运算符new,以及一个能够完成清理与释放内存的运算符delete。

  1. 从内存池申请一些内存需要用new,它将根据你提供的数据类型分配一块大小适当的内存。
  2. 如果有足够的可用内存,new语句将返回新分配地址块的起始地址。
  3. 如果没有足够的可用内存空间,就会抛出std::bad_aloc异常。

注意:在用完内存块之后,应该用delete语句释放。另外附加一种保险的措施,在释放了内存块之后,还应该把与之关联的指针设置为NULL。

用new运算符实现动态内存分配

int* p1 = new int;     // 动态分配 4 个字节( 1 个 int)的空间单个数据
int* p2 = new int(10);    // 动态分配 4 个字节( 1 个 int)的空间并初始化为 10
int* p3 = new int[10];    // 动态分配 40 个字节( 10 个 int)的空间

例:

class AA
{
public:
      AA()
      {
            cout << "AA()" << endl;
      }
      ~AA()
      {
            cout << "~AA()" << endl;
      }
private:
      int _a;
};
int main()
{        
      AA* p1 = new AA[10];
      delete[] p1;   //为什么知道析构多少次?    
      //因为多开了4个字节的空间
}

int main()
{
      AA* p0 = new AA;
      //free(p0);   //不会崩, 未调析构   可能会内存泄漏
      delete[] p0;  //会崩,会越界  误以为多开了4个字节的空间
      return 0;
}

int main()
{
      AA*  p1 = new AA[10];
      //free(p1);//会崩   释放错位置
      //delete p1;  //会崩   释放错位置  在屏蔽了自定义类型里写的构造和析构后  又不会崩
      //严格来说 new[]一定要多开4个字节,但编译器会优化,编译器识别到析构函数可调可不调的时候,就不用多开4个字节
      //要是自己写了析构,则编译器一定会调,不匹配就会出错
      delete[] p1;
}

int main()
{
      //这种情况内置类型不匹配不会崩
      int* p2 = new int[40];  //不会多开4个字节
      delete p2;  // 编译器优化  编译器识别到析构函数可调可不调的时候  就不用多开4个字节
      return 0;
}

有关operator new/operator delete operator new[]/operator delete[]
总结:

  1. operator new/operator delete operator new[]/operator delete[]的用法和malloc/free一样。
  2. 它们只负责分配空间/释放空间,不会调用对象构造函数和析构函数来初始化/清理对象。
  3. 实际operator new/operator delete 只是malloc和free的一层封装。

malloc()/free()和new/delete的区别和联系

  • 它们都是动态管理内存的入口。
  • malloc/free是C/C++标准库函数,new/delete是C++操作符。
  • malloc/free只是动态分配内存空间/释放空间,而new/delete除了分配空间还会调用构造函数和析构函数进行初始化和清理工作。
  • malloc/free需要手动计算类型大小且返回值为void*,new/delete可自己计算类型的大小,返回对应类型的指针。
  • malloc失败返回NULL,new失败抛异常。

常见的内存错误及避免

1、使用未分配成功的内存

  • 在使用内存之前检查指针是否为NULL

2、引用分配成功但尚未初始化的内存

  • 赋初值,即便是赋零值也不可省略

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

  • 注意下标的使用不能超出边界

4、忘记释放内存,造成内存泄露

  • 动态内存的申请与释放必须配对,程序中malloc与free的使用次数一定要相同(new与delete同理),否则会出现问题。

5、释放了内存却继续使用它

  • 使用free或delete释放了内存后,将指针设置为NULL。若没有设置为NULL,就会产生“野指针”。

内存错误的注意点

  • 指针消亡了,但并不表示它所值的内存被自动释放。
  • 内存被释放了,并不表示指针会消亡或者为NULL指针。

野指针

  • 野指针的形成是指针变量没有被初始化,任何指针变量刚被创建时不会自动称为NULL指针,它的缺省是随机的。
  • 指针变量在创建的同时应该被初始化,要么将指针设置为NULL,要么让它指向合法内存。
  • 使用free或delete释放了内存后,将指针设置为NULL。若没有设置为NULL,就会产生“野指针”。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值