动态内存管理(想要了解动态内存管理,只需要看这一篇就足够了!)

简介: 动态内存管理(想要了解动态内存管理,只需要看这一篇就足够了!)

那么不废话,让我们直接开始~~~

接下来我会从大致这三个方面来讲解动态内存管理:



1.动态内存管理的基本概念

       动态内存管理:它是C语言中一项重要的编程任务,它使得程序在运行时能够灵活地分配和释放内存,更好地适应不同的运行条件,通过动态内存管理,我们可以使用内存更高效、更灵活。

为了让读者能够更好的理解,现将其拆解开来理解:

       动态:即我们可以将申请的空间自由的变大变小以适应需求;

       内存:即我们申请的对象是内存空间;

       管理:我们需要对申请的内存空间进行管理操作;

2.为什么要有动态内存管理

       首先让我们来看一个实例:

int n = 1;      //在栈空间上开辟4个字节
int arr[5] = { 0 }; //在栈空间上开辟20个字节的连续空间

从上边的实例我们可以看出,我们创建变量是开辟空间的大小是固定的;当我们在船舰数组的时候,必须指定数组的长度,并且数组空间一旦确定了大小将不能调整其大小。但是有时候我们需要的空间大小是在程序运行的时候才能知道,那么上述创建空间大小的方式就不太适合了。

       因此引入了动态内存开辟的方式,让程序员自己可以申请和释放空间,这也就是为什么要有动态内存管理的原因。

3.动态内存的申请和释放

       现在我们已经了解动态内存管理的基本概念和为什么要有动态内存管理,那么接下来就是如何去申请动态内存,并且对其进行操作了。

       对于申请和释放动态内存空间,我们有这三个函数实现:malloc、free、calloc、realloc

       1.malloc

先让我们看一下官网对malloc函数的解释:

解释如下:

       1.该函数是向内存申请一块连续可用的空间,并返回指向这块空间的指针(首位置的地址);

       2.如果开辟空间成功,则返回一个指向开辟好空间的指针;

       3.如果开辟失败,则返回一个NULL 指针;

       4.申请空间的大小的单位是字节,如果参数size 为0,malloc的行为是标准是未定义的,取决于编译器。

       5.使用该函数需要包含<stdlib.h>头文件

接下来让我们使用一个实例助你更好的了解malloc函数(如图):

//向内存申请20个字节大小的空间,并放入5个整数
#include<stdio.h>
#include<stdlib.h>
int main()
{
  int* p = (int*)malloc(20);
  if (p == NULL)
  {
    perror("malloc");
    return 1;
  }
  for (int i = 0; i < 5; i++)
  {
    *(p + i) = i + 1;
  }
  for (int k = 0; k < 5; k++)
  {
    printf("%d ", *(p + k));
  }
  return 0;
}

这里有两个注意点需要我们注意:

       1.我们将malloc的返回值进行了强制转换(返回值是void * 类型,但是我们将其强制转换成了int *,原因是我们已经知道了申请的空间中我们要放入什么数据,为了便于下面的操作,我们进行了对指针的强制转换);

       2.使用指针接收返回值后,我们判断了指针是否为NULL(原因是如果开辟失败,则返回一个NULL 指针,我们不能对NULL指针进行操作,所以直接停止程序);

至此我们就大致的了解了malloc函数的使用。

       2.free

有借有还,再借不难,我们向内存申请空间使用完成之后,就需要将申请的空间归还给操作系统,那么我们就需要使用free函数。

先让我们看一下官网对malloc函数的解释:

用一句话解释就是如果想要释放申请的空间,就将接收申请空间的地址的指针填入即可();

注:想要使用free函数释放申请内存空间,必须传递接收申请空间的地址的指针(即申请空间时返回值的地址,不能放入其他位置的地址)

       现在我们将上面的例子改进一下:

#include<stdio.h>
#include<stdlib.h>
int main()
{
  int* p = (int*)malloc(20);
  if (p == NULL)
  {
    perror("malloc");
    return 1;
  }
  for (int i = 0; i < 5; i++)
  {
    *(p + i) = i + 1;
  }
  for (int k = 0; k < 5; k++)
  {
    printf("%d ", *(p + k));
  }
  free(p);
  p = NULL;
  return 0;
}

从上面代码我们发现free完申请的空间之后,我们将接收申请空间的地址的指针置为了NULL,原因是放置该指针变为野指针。

至此我们就大致的了解了free函数的使用。

       3.calloc

calloc函数的使用以及用处和malloc大致相同,只有些许细节略微有些不同。

先让我们看一下官网对calloc函数的解释:

仿照着malloc函数,我们对calloc函数解释一下:

解释如下:

       1.函数的功能是开辟一块大小为num 个大小为size 的内存空间,返回指向这块空间的指针,并且把空间的每个字节初始化为0;

       2.如果开辟空间成功,则返回一个指向开辟好空间的指针(首位置的地址);

       3.如果开辟失败,则返回一个NULL 指针;

       4.申请空间的大小的单位是字节,如果参数size 为0,malloc的行为是标准是未定义的,取决于编译器。

       5.使用该函数需要包含<stdlib.h>头文件

我们使用一个实例进行进一步加深理解:

#include<stdio.h>
#include<stdlib.h>
int main()
{
  int* p = (int*)calloc(5, sizeof(int));
  if (p == NULL)
  {
    perror("malloc");
    return 1;
  }
  for (int i = 0; i < 5; i++)
  {
    *(p + i) = i + 1;
  }
  for (int k = 0; k < 5; k++)
  {
    printf("%d ", *(p + k));
  }
  free(p);
  p = NULL;
  return 0;
}

事实上calloc函数在使用的形式上只是函数名称和参数改变了一下,其他与malloc函数全部一样。

注意:

       malloc函数和calloc函数内部的不同体现在了是否会把申请的内存空间的每个字节初始化为0!!!

如图所示:

malloc函数对申请的内存空间不进行初始化:

malloc函数对申请的内存空间进行初始化为0:

以上即malloc函数和calloc函数在存储内部的不同!!!

       4.realloc

讲了这么多对动态内存空间的申请,但是不是说可以对申请的空间大小进行修改吗?那么如何修改呢?

这就需要使用realloc函数:

先让我们看一下官网对realloc函数的解释:

我们对该函数进行讲解:

讲解如下:

       1.该函数会返回修改之后的内存空间的地址;

       2.ptr 是要调整的内存地址(即申请动态内存空间时返回值的地址),size 为调整之后新大小;

       3.如果修改空间成功,则返回一个指向修改好的空间的指针(首位置的地址);

       4.如果修改失败,则返回一个NULL 指针;

但是既然我们想要修改申请内存空间的大小,就可能会增加申请空间的大小,但由于我们申请的空间是连续的,就可能会遇到想要扩增空间时,后面的空间被使用的情况(如图):

那么这个时候该怎么扩增空间呢?

所以我们就需要分开进行讨论:

       1.后续空间充足时:

               要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化

       2.后续空间不充足时:

               其扩展的方法是:在堆空间上另找一个合适大小的连续空间,将已有数据先复制过去后,在进行扩增,然后返回一个新的内存地址。

以上我将就了解了realloc函数的使用和其是如何进行扩增空间的。那么我们接下来使用一个实例来使你更好的理解realloc函数:

//向内存申请20个字节大小的空间,并放入5个整数之后,
//将空间扩增为40字节大小,并接着上面的数字继续进行赋值
#include<stdio.h>
#include<stdlib.h>
int main()
{
  int* p = (int*)malloc(20);
  if (p == NULL)
  {
    perror("malloc");
    return 1;
  }
  for (int i = 0; i < 5; i++)
  {
    *(p + i) = i + 1;
  }
  for (int k = 0; k < 5; k++)
  {
    printf("%d ", *(p + k));
  }
  printf("\n");
  int* pc = (int*)realloc(p, 40);
  if (pc != NULL)
  {
    p = pc;
    pc = NULL;
    for (int i = 5; i < 10; i++)
    {
      *(p + i) = i + 1;
    }
    for (int i = 0; i < 10; i++)
    {
      printf("%d ", *(p + i));
    }
  }
  free(p);
  p = NULL;
  return 0;
}

从上面代码我们可以看到我们扩增完空间后,判断了其接收的指针是否是NULL之后才继续让p指针管理扩增后的空间,这样做的目的是为了防止扩增失败后返回NULL,可能会将之前管理内存空间的指针变为NULL,使之前开辟的空间失效。

以上我们就理解了realloc函数。

总结

       内存管理是一项非常重要的任务。动态内存管理是指在程序运行时分配和释放内存的过程。通过动态内存管理,我们可以根据需要分配适当的内存空间,并在不再需要时释放它。这使得程序更加灵活,并能够处理各种大小和形状的数据。


以上就是动态内存管理的所有内容了~~~

相关文章
|
10天前
|
算法 Java 程序员
Python内存管理用引用计数(对象的`ob_refcnt`)跟踪对象,但循环引用(如A-&gt;B-&gt;A)可导致内存泄漏。
【6月更文挑战第20天】Python内存管理用引用计数(对象的`ob_refcnt`)跟踪对象,但循环引用(如A-&gt;B-&gt;A)可导致内存泄漏。为解决此问题,Python使用`gc`模块检测并清理循环引用,可通过`gc.collect()`手动回收。此外,Python结合标记清除和分代回收策略,针对不同生命周期的对象优化垃圾回收效率,确保内存有效释放。
15 3
|
1月前
|
算法 Java Python
【Python 的内存管理机制专栏】Python 内存管理实战:性能优化与内存泄漏检测
【5月更文挑战第18天】Python内存管理关乎程序性能与稳定性。优化包括避免过多临时对象,如优化列表推导式减少对象创建。警惕循环引用造成的内存泄漏,如示例中的Node类。使用`gc`模块检测泄漏,通过`gc.set_debug(gc.DEBUG_LEAK)`和`gc.collect()`获取信息。实践中需持续分析内存使用,优化算法、数据结构和资源释放,以提升程序质量与效率。
【Python 的内存管理机制专栏】Python 内存管理实战:性能优化与内存泄漏检测
|
4天前
|
C++
C/C++内存管理(1):C/C++内存分布,C++内存管理方式
C/C++内存管理(1):C/C++内存分布,C++内存管理方式
|
1月前
|
算法 内存技术
深入理解操作系统内存管理:从虚拟内存到物理内存的旅程
【5月更文挑战第24天】 在现代计算机系统中,操作系统的内存管理是确保系统高效稳定运行的关键组成部分。本文将探讨操作系统是如何通过虚拟内存到物理内存的映射机制,实现对内存资源的高效管理和保护。我们将剖析分页和分段两种主要的内存管理技术,并讨论它们如何协同工作以提供内存抽象、重定位、共享和保护。文章还将涉及虚拟内存的技术细节,包括页面置换算法和内存分配策略,以及它们对系统性能的影响。
|
1月前
|
存储 缓存 程序员
C++内存管理:避免内存泄漏与性能优化的策略
C++内存管理涉及程序稳定性、可靠性和性能。理解堆和栈的区别至关重要,其中堆内存需手动分配和释放。避免内存泄漏的策略包括及时释放内存、使用智能指针和避免野指针。性能优化策略则包括减少内存分配、选用合适数据结构、避免深拷贝及缓存常用数据。通过这些最佳实践,可提升C++程序的效率和质量。
|
1月前
|
程序员 编译器 C语言
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(下)
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free
28 0
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(下)
|
1月前
|
编译器 数据库 C语言
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(上)
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free
26 0
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(上)
|
1月前
|
存储 缓存 算法
深入理解操作系统内存管理:从虚拟内存到物理内存
【5月更文挑战第30天】操作系统的心脏——内存管理,在系统性能和稳定性中扮演着关键角色。本文将深入探讨操作系统中的内存管理机制,特别是虚拟内存与物理内存之间的映射关系、分页机制以及内存分配策略。通过分析现代操作系统如何处理内存资源,我们可以更好地理解计算机系统的内部工作原理,并掌握提升系统性能的关键因素。
|
1月前
|
C语言 C++
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(中)
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free
26 0
|
6天前
|
存储 Java C++
Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据
Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据,如局部变量和操作数;本地方法栈支持native方法;堆存放所有线程的对象实例,由垃圾回收管理;方法区(在Java 8后变为元空间)存储类信息和常量;运行时常量池是方法区一部分,保存符号引用和常量;直接内存非JVM规范定义,手动管理,通过Buffer类使用。Java 8后,永久代被元空间取代,G1成为默认GC。
18 2