C/C++ 动态分配:malloc()和free()所涉及的空指针和强制类型转换、与new和delete的对比、内存泄露是仅用指针来操作和锚定动态内存所导致的风险

1、动态分配的内涵

所谓动态内存分配,是指在程序运行时根据需要分配和释放内存,而不是在编译时确定内存需求。动态分配包括两方面的内涵:

  • 在堆上分配内存
    对于linux的虚拟内存,可以分成以下5段:文本段、数据段(分初始化和未初始化数据段)、堆和栈。不使用动态分配定义一个变量,这个变量位于数据段(比如全局变量和静态变量)或者栈(比如函数体中的自动变量)中。关于虚拟内存,堆和栈,以后再专门开帖子讲。
  • 在运行阶段而不是编译阶段分配内存
    对比:#define和typedef分别是在预编译和编译阶段工作。

2、malloc()和free()

2.1 空指针和强制类型转换

malloc()和free()是c语言的动态内存分配函数。具体定义如下:

void* malloc(size_t size);

void free(void* ptr);

注意malloc()返回的是空指针/通用指针。实际上,为了增加函数的泛用性,许多库的封装函数都返回的空指针:创建成功时,返回指向该内存的空指针;若失败,则返回NULL。返回空指针的好处是,在使用时可以通过数据类型强制转换,将空指针转换成任意类型的指针,这极大提高了函数的泛用性。返回空指针空指针的函数一般是通过强制转换的方式进行使用,如下例所示:

int *arr = (int *)malloc(10 * sizeof(int));  //返回的空指针强转为int型指针

if (arr == NULL)     // 检查内存分配是否成功

{

printf("内存分配失败\n");

return 1;

}
......

free(arr);     

2.2 等式两边的独立性

使用malloc()在堆中分配内存时,除了空指针和强制类型转换,还有必要分别对等式左右两边的语句及其关系进行说明。

int *ptr = (int *)malloc(10 * sizeof(int));

等式左边定义了一个int型指针arr,等式右边调用malloc()函数在堆中分配了一个长度为10的int型数组。等式两边是彼此独立的两个语句,因此也可以把它们拆开写,语法上也是成立的:

int *ptr;
(int *)malloc(10 * sizeof(int));

但是这样做是不妥的,因为这么做涉及到动态分配的另一个关键特性:动态分配的变量(包括数据、结构体等)是没有变量名的,必须在定义时将其地址赋给一个指针,使用指针来操作变量。如果使用malloc()单独进行内存分配,那么所分配的内存是无法操作的,因为既不知道变量名,也不知道其具体位置。因此,实际进行内存分配时,都是指针定义和动态内存分配写在一个式子里边的。

3、new和delete

c++中,使用new和delete进行内存动态分配。和malloc()和free()一样,等式两边是彼此独立的两个语句,同样可以把它们拆开写。

和malloc()和free()不同的是,malloc()和free()是函数,但new和delete是关键字。并且new 关键字不仅仅分配内存,还会调用对象的构造函数来初始化对象。

另外还有一点很关键:delete释放的是堆中内存,而不是指向该内存的指针本身,后续该指针可以被继续使用

动态分配基本数据类型的变量:

int *ptr = new int; 

……

delete ptr;

动态分配数组:

int *ptr = new int [10];

……

delete [] ptr;

4、内存泄露

内存泄漏(Memory Leak)是指在计算机程序中,已分配的内存由于某种原因未能被释放,导致内存资源逐渐耗尽的现象。其实就是使用malloc()和free()(cpp中是new和delete)所存在的风险,更具体地说,是仅用指针来操作和锚定内存所存在的风险

发生内存泄露存在两种常见的情况:

  • 在函数中分配了一个堆,但是函数结束前忘记了使用free()或delete释放内存。所以malloc()和free()一定要成对使用。
    这是什么道理呢?我们知道,在函数体中定义的变量是自动变量,存放在对应的函数栈中,其生命期会随着函数调用的结束而结束。因此,随着函数调用的结束,在函数体内定义的用于操作和锚定动态内存的指针也就随之湮灭了。
    使用malloc()和new动态分配内存时,是在堆中进行分配,而不是分配在函数栈中。因此,即便动态分配所创建的内存并不会随着函数调用的结束而自动释放。
    于是就造成了这么一个情况:如果没有调用free()或delete及时释放内存,那么函数调用结束之后,原本唯一用于操作和锚定内存的指针消失了,而没有被释放的堆中内存就会一直存在下去,并且没有任何手段能找到它,这块内存成为了无用的内存碎片。每调用一次函数,就会产生一个内存碎片,久而久之,当内存被分割得支离破碎,再也找不到可被有效利用的内存块时,就会发生内存泄露。
  • 指向堆的指针被覆盖,指向了其他地方,导致无法再找到所分配的堆内存。
    对于这种情况,可以使用const常量指针。如果试图将pt指向别处,那么系统将会编译报错。
int* const ptr = (int *)malloc(10 * sizeof(int));  //c
int* const ptr = new int;  //cpp
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值