C/C++动态内存管理

本文详细探讨了C语言中的malloc/free与C++中的new/delete之间的差异与联系,包括它们的功能、内部实现机制以及如何处理动态内存分配和释放。此外,还介绍了new/delete与new[]/delete[]的区别,并给出了模拟new[]/delete[]的宏实现。
摘要由CSDN通过智能技术生成

1. malloc/free和new/delete之间关系和差异

(1)在C语言中,使用malloc/realloc/calloc/free进行动态内存管理。其中,malloc/realloc/calloc用于动态内存开辟,free用于内存释放。

  • malloc —— 原型为void* malloc(size_t size);在内存中的动态存储区中分配一个长度为size的空间,返回指向大小为size的内存区域首地址的void指针;用户必须决定对象的长度,即申请空间的大小。同时,malloc只是分配了一块返回值为void*的内存而不是生成一个对象。由于malloc返回值的类型是void*,所以在调用malloc时要显式地进行类型转换,将void*转换成所需要的指针类型,malloc函数本身并不识别要申请的内存是什么类型,它只关心内存的总字节数,因此使用sizeof是很好的方式,例如:int* p = (int*) malloc(sizeof(int) * length);使用malloc所分配的是一块连续的内存,同时由于编译器的实现问题(比如边界对齐等),其所分配的内存可能比所请求的多一点。

    如果内存池为空,或者可用内存不能满足所请求的内存,则编译器向操作系统请求,要求得到更多的内存,并在所得到的内存上执行分配任务。若操作系统无法向malloc提供更多的内存,那么malloc返回一个NULL指针,因此有必要对malloc的返回指针进行检查。

  • realloc —— 原型为void realloc(void* ptr, size_t sz);用于修改一个原先已经分配内存块的大小,可以扩大也可以缩小。若扩大时,则保留原先的内存,将新添加的内存放于原先内存块的后面,新内存并未以任何方式进行初始化;若缩小时,该内存块尾部的部分内存释放,其余部分保留。

    注意:若realloc的第一个参数为NULL,那么其行为与malloc一样。另外如果原先的内存块无法改变大小,realloc将分配另一块正确大小的内存,并把原先那块内存的内容复制到新块上,因此在使用realloc之后就不能再使用指向原内存的指针,而应该改用realloc所返回的新指针。

  • calloc —— 原型为void* calloc(size_t elem_num, size_t elem_sz);与malloc的主要区别在于calloc在返回指向内存的指针之前把它初始化为0,另一个区别是他们请求内存数量的方式不通,calloc的参数包括所需元素的数量和每个元素的字节数,由此它能够计算出总共需要分配的内存。

    -free —— 原型为void free( void* p ); 由于指针p的类型以及它所指的内存的容量事先都是知道的,因此语句free(p)能正确地释放内存。如果p是NULL指针,那么free对p无论操作多少次都不会出问题。如果p不是NULL指针,那么free对p连续操作两次就会导致未定义的结果。

    下面给出一段代码:

void test()
{
    int* p1 = (int*)malloc(sizeof(int)*4);
    int* p2 = (int*)calloc(4, sizeof(int));
    int* p3 = (int*)realloc(p2, sizeof(int)* 6);

    free(p1);
    free(p3);
}

(2)在C++语言中,new用于动态内存开辟,delete用于内存释放。

  • 由于对象在创建的时候要调用构造函数进行初始化,而在对象在消亡的时候要调用析构函数进行一些清理工作,显然,malloc/free无法完成这些事情。因此,在C++中提供了new/delete两个操作符,用new在动态分配内存的时候调用构造函数进行初始化,delete在释放内存的时候自动调用析构函数进行清理。

    下面写出一个代码:

void test()
{
    int* p4 = new int;//4个字节动态分配4个字节(1个int)的空间单个数据
    int* p5 = new int(3);//动态分配4个字节(1个int)的空间并初始化为3 
    int* p6 = new int[3];//动态分配12个字节(3个int)的空间

    delete p4;
    delete p5;
    delete[] p6;
}

这里写图片描述

new和delete的内部实现

  • new通过调用下面的函数实现:
char *  operatoer new(size_t count);

这里写图片描述

new做了两件事:
①调用operator new分配空间。
②调用构造函数初始化对象。

  • delete通过调用下面的函数实现:
void operator delete(char *p);

这里写图片描述

delete做了两件事:
①调用析构函数清理对象。
②调用operator delete释放空间。

(3)总结malloc/free和new/delete的区别与联系

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

2. new/delete和new[]/delete[]

对比上述的new/delete的用法及所做事情,我们接下来阐述一下new[]/delete[]的具体实现方法。

  • new[]通过调用下面的函数实现:
char * operator new[](size_t count);

这里写图片描述

new[N]做了两件事情:
①调用operator new分配空间。
②调用N次构造函数分别初始化每个对象。

  • delete[]通过调用下面的函数实现:
void operator delete[](char *p);

这里写图片描述

delete[N]做了两件事情:
①调用N次析构函数清理对象。
②调用operator delete释放空间。

3.实现NEW_ARRAY/DELETE_ARRAY宏,模拟new[]/delete[]申请和释放数组

(1)NEW_ARRAY宏——模拟实现new[]

#define NEW_ARRAY(PTR,TYPE,N)                      \
do                                                 \
{                                                  \
    PTR = (TYPE*)operator new(sizeof(TYPE)*N + 4); \
    (*(int*)PTR) = N;                              \
    PTR = (TYPE*)((char*)PTR + 4);                 \
    for (size_t i = 0; i < N; i++)                 \
        new(PTR + i)TYPE;                          \
}while (false);

(2)DELETE_ARRAY宏——模拟实现delete[]

#define DELETE_ARRAY(PTR,TYPE)         \
do                                                            \
{                                                               \
    size_t N = *((int*)PTR - 1);               \
    for (size_t i = 0; i < N; ++i)              \
        PTR[i].~TYPE();                         \
    PTR = (TYPE*)((char*)PTR - 4);         \
    operator delete(PTR);                      \
}while (false);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值