动态内存管理-1

简介: 动态内存管理

一. 为什么要有动态内存分配

我们已经掌握的内存开辟⽅式有:

 int val = 20;//在栈空间上开辟四个字节
 char arr[10] = {0};//在栈空间上开辟10个字节的连续空间

但是上述的开辟空间的⽅式有两个特点:


(1)空间开辟⼤⼩是固定的。


(2)数组在申明的时候,必须指定数组的⻓度,数组空间⼀旦确定了⼤⼩不能调整 但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间⼤⼩在程序运⾏的时候才能知 道,那数组的编译时开辟空间的⽅式就不能满⾜了。 C语⾔引⼊了动态内存开辟,让程序员⾃⼰可以申请和释放空间,就⽐较灵活了。


二. malloc和free

2.1 malloc

C语⾔提供了⼀个动态内存开辟的函数:

void* malloc (size_t size);

这个函数向内存申请⼀块连续可⽤的空间,并返回指向这块空间的指针。

• 如果开辟成功,则返回⼀个指向开辟好空间的指针。

• 如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查。

• 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使⽤的时候使⽤者⾃

⼰来决定。

• 如果参数 size 为0,malloc的⾏为是标准是未定义的,取决于编译器。

279c2aeb112f75bac89b3a6a52e24105_a38f4bb0b4124233b7d6ce74cf7db01f.png

注意:malloc在堆区申请内存空间,如果需要释放空间,需要使用free函数,如果没有使用free释放,在程序退出时,也会由操作系统来回收。


2.2  free

C语⾔提供了另外⼀个函数free,专⻔是⽤来做动态内存的释放和回收的,函数原型如下:

void free (void* ptr);

free函数⽤来释放动态开辟的内存。


• 如果参数 ptr 指向的空间不是动态开辟的,那free函数的⾏为是未定义的。

• 如果参数 ptr 是NULL指针,则函数什么事都不做。

  malloc和free都声明在 stdlib.h 头⽂件中。

注:当使用free释放空间时,例如:free(p)   ,要及时将p=NULL,因为此时p指向的空间被释放了,p变成野指针了。


三. calloc和realloc

3.1 calloc

C语⾔还提供了⼀个函数叫 calloc , calloc 函数也⽤来动态内存分配。原型如下:

 void* calloc (size_t num, size_t size);

• 函数的功能是开辟num 个⼤⼩为 size个字节的空间,并且把空间的每个字节初始化为0。


• 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0.

所以如果我们对申请的内存空间的内容要求初始化,那么可以很⽅便的使⽤calloc函数来完成任务。


3.2 realloc

• realloc函数的出现让动态内存管理更加灵活。

• 有时会我们发现过去申请的空间太⼩了,有时候我们⼜会觉得申请的空间过⼤了,那为了合理的时候内存,我们⼀定会对内存的⼤⼩做灵活的调整。那 realloc 函数就可以做到对动态开辟内存⼤

⼩的调整。

函数原型如下:


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

• ptr 是要调整的内存地址


• size 调整之后新⼤⼩

• 返回值为调整之后的内存起始位置。

• 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。

• realloc在调整内存空间的是存在两种情况:

    ◦ 情况1:原有空间之后有⾜够⼤的空间

    ◦ 情况2:原有空间之后没有⾜够⼤的空间

3a451a50a3929fe2c5edf1bc4b010c2c_0bec7aeb6e4d46ca8307376e713ef30b.png

情况1:

当是情况1 的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发⽣变化。

情况2:

当是情况2 的时候,原有空间之后没有⾜够多的空间时,扩展的⽅法是:在堆空间上另找⼀个合适⼤⼩的连续空间来使⽤。这样函数返回的是⼀个新的内存地址。

由于上述的两种情况,realloc函数的使⽤就要注意⼀些。

注意:当realloc的第一个参数为NULL时,功能相当于malloc

realloc(NULL,40);//malloc(40)


四. 常见的动态内存的错误

4.1 对NULL指针的解引用操作

void test()
{
  int* p = (int*)malloc(INT_MAX / 4);
   * p = 20;//如果p的值是NULL,就会有问题
   free(p);
  }

如果malloc开辟空间失败,就会返回空指针,此时会对空指针解引用,正确的做法要在解引用前对malloc的返回值进行检测。


4.2 对动态开辟空间的越界访问

int main()
 {
  int i = 0;
  int* p = (int*)malloc(10 * sizeof(int));
   if (NULL == p)
     {
     return 1;
     }
   for (i = 0; i <= 10; i++)
     {
    * (p + i) = i;//当i是10的时候越界访问
     }
   free(p);
  }

输出结果:93fdc9023185904456a23b360b900ead_2915b55ff36d43499b517e94e4957a1d.png


在i=10的时候就出现越界访问了。


4.3 对非动态开辟内存使用free释放

int main()
{
int a = 10;
int *p = &a;
free(p);
}

解析:同样的,对非动态开辟内存用free释放,也会导致程序崩溃

4.4 使用free释放一块动态开辟内存的一部分

int main()
{
 int *p = (int *)malloc(100);
p++;
free(p);
 }

p不再指向动态内存的起始位置,这样会出错,使用free时,必须指向动态开辟内存的起始位


置。


4.5 对同⼀块动态内存多次释放

 int main()
 {
int *p = (int *)malloc(100);
 free(p);
 free(p);//重复释放
 }

重复释放会出现问题,如何避免这个问题,在free一次后,赋值NULL后,再次free就没有问题了。


4.6 动态开辟内存忘记释放(内存泄漏)

void test()
 {
int *p = (int *)malloc(100);
if(NULL != p)
 {
 *p = 20;
 }
 }
 int main()
{
 test();
 while(1);
 }

忘记释放不再使⽤的动态开辟的空间会造成内存泄漏。

切记:动态开辟的空间⼀定要释放,并且正确释放。

相关文章
|
算法 计算机视觉 索引
python---OpenCv(二),背景分离方法较有意思
python---OpenCv(二),背景分离方法较有意思
226 1
|
11月前
|
存储 Java
BIO的工作流程
BIO(Blocking I/O)工作流程是指在进行输入输出操作时,线程会一直阻塞直到操作完成。具体流程包括:客户端发起请求,服务器接收后开始处理,期间服务器线程处于等待状态,直至数据准备完毕,响应返回给客户端,线程才继续执行其他任务。
216 5
|
11月前
|
XML JavaScript 前端开发
如何解析一个 HTML 文本
【10月更文挑战第23天】在实际应用中,根据具体的需求和场景,我们可以灵活选择解析方法,并结合其他相关技术来实现高效、准确的 HTML 解析。随着网页技术的不断发展,解析 HTML 文本的方法也在不断更新和完善,
|
12月前
|
监控 JavaScript 前端开发
前端的混合之路Meteor篇(六):发布订阅示例代码及如何将Meteor的响应数据映射到vue3的reactive系统
本文介绍了 Meteor 3.0 中的发布-订阅模型,详细讲解了如何在服务器端通过 `Meteor.publish` 发布数据,包括简单发布和自定义发布。客户端则通过 `Meteor.subscribe` 订阅数据,并使用 MiniMongo 实现实时数据同步。此外,还展示了如何在 Vue 3 中将 MiniMongo 的 `cursor` 转化为响应式数组,实现数据的自动更新。
170 2
|
IDE 搜索推荐 Devops
|
人工智能 安全 物联网
未来智能家居技术的发展与挑战
智能家居技术作为当今科技发展的重要分支,正在以惊人的速度改变着人们的生活方式。本文将探讨未来智能家居技术的发展趋势及面临的挑战,从人工智能、物联网、数据安全等方面展开讨论,以期为读者呈现一个全面的未来智能家居技术发展的画卷。
167 27
|
XML Java 应用服务中间件
Filter 过滤器--基本原理--Filter 过滤器生命周期--过滤器链--注意事项和细节--全部应用实例--综合代码示例
Filter 过滤器--基本原理--Filter 过滤器生命周期--过滤器链--注意事项和细节--全部应用实例--综合代码示例
405 0
|
消息中间件 存储 Apache
RocketMQ实战教程之常见概念和模型
Apache RocketMQ 实战教程介绍了其核心概念和模型。消息是基本的数据传输单元,主题是消息的分类容器,支持字节、数字和短划线命名,最长64个字符。消息类型包括普通、顺序、事务和定时/延时消息。消息队列是实际存储和传输消息的容器,是主题的分区。消费者分组是一组行为一致的消费者的逻辑集合,也有命名限制。此外,文档还提到了一些使用约束和建议,如主题和消费者组名的命名规则,消息大小限制,请求超时时间等。RocketMQ 提供了多种消息模型,包括发布/订阅模型,有助于理解和优化消息处理。
|
人工智能 自然语言处理 程序员
AI写代码的体验:与通用灵码的使用感受分享
近两年随着人工智能的飞速发展,它正在迅速改变我们的生活和工作方式,作为程序员,编写代码是我日常工作的一部分,我觉得生成式AI对日常开发工作有很大的帮助,其中之一就是通过AI编写想要的代码。尤其是当我了解到人工智能技术已经发展到可以辅助程序员写代码的程度时,我对此感到非常开心,因为可以帮助我提高开发效率。所以在去年我就尝试使用代码生成的AI工具,而最近我看到阿里云的通义灵码又做了重大更新,所以我决定尝试使用通义灵码来编写代码,并在这个过程中获得一些有趣的体验。那么在本文中,就来简单分享一下使用通义灵码进行代码编写的体验和感受。
789 10
AI写代码的体验:与通用灵码的使用感受分享