动态内存管理(malloc、free、calloc、realloc函数简介)

简介: 动态内存管理(malloc、free、calloc、realloc函数简介)

🎃为什么存在动态内存管理

在此之前,我们开辟内存空间有两种方式。一种是创建一个已知类型的变量。

比如说:

int a=10;  //在栈空间上开辟4个字节

向系统申请了4个字节的内存空间。(对于 int型,4个字节它是固定的。)

还有一种是,创建一个数组

比如说:

int arr[10]; //在栈空间上开辟40个字节的连续空间。

向系统申请了40个字节的内存空间。当这个数组开辟好了空间,没有办法改变它的大小。

对于数组的创建,它的内存开辟方式是比较死板的。

int arr1[10];
int arr2[100];

我们创建数组时,在一开始时就会指定数组的大小。arr1的内存空间为40个字节,可以存放10个整型元素。arr2的内存空间为400个字节,可以存放100个整型元素。

但有可能我们在使用数组arr1的时候,需要存放11个数组元素,而没有办法把它边长。

我们可能为了尽可能满足很多情况,而创建一个很大的数组arr2,但在实际使用过程中,我们可能只会存放20个元素,而导致了内存空间的浪费。

所以这样的内存开辟方式它是固定的,是不够灵活的

不仅仅是上述的情况,有时候我们需要的空间大小在程序运行的时候才能知道,那么数组在编译时开辟空间的方式就不能满足了。

所以,我们需要学会开辟动态内存

🎃动态内存函数的介绍

malloc
free
calloc
realloc

🎊malloc

malloc函数的原型:

void* malloc(size_t size);

malloc声明在stdlib.h头文件中。

功能:

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

  • 如果开辟成功,则返回指向这块空间的指针。
  • 如果开辟失败,则返回一个NULL指针。

因此,malloc函数的返回值一定要做检查。

举个例子:

原来,我们用数组栈区开辟内存空间:

现在,我们在堆区动态开辟同样大小的内存空间:

根据malloc函数的原型,我们需要传递一个参数,以字节为单位的内存。

malloc(40)//即开辟了40个字节的内存空间

然后,我们需要一个指针p来指向这块儿开辟好的连续的内存空间

但由于 malloc函数的返回值为 void*,即无类型指针,所以我们需要先进行强制转换,将无类型指针转换为整型指针

因此,

int* p=(int*)malloc(40);

但是,正如我们上面所提到的,我们只是用malloc函数向内存申请开辟40个自己的连续空间,不一定开辟成功。所以我们需要利用指针p进行进一步检验

若开辟成功,进行访问:

malloc函数 申请的内存空间,但程序退出时,不会主动释放的,需要使用free函数来释放。

🎋perror函数

perror函数(忘的打印输出函数)

来自这篇博客:C语言perror函数详解

🎊free

C语言提供free函数,专门用来做动态内存释放和回收的。

free函数原型:

void free(void *ptr);

free函数也是声明在头文件中的。

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

  • 如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的。
  • 如果参数ptr是NULL指针,则函数什么事都不做。

🎊calloc

C语言还提供了一个函数叫calloc,calloc函数也用来动态内存分配。

calloc函数的原型:

void* calloc(size_t num,size_t size);

calloc函数的功能为,为num个大小为size的元素开辟一块空间,并且把每个字节初始化为0。

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

即:

#include<stdio.h>
#include<stdlib.h>
int main()
{
  int* p = (int*)calloc(10, sizeof(int));//开辟10个大小为sizeof(int),即4个字节 的空间
  //判断是否开辟成功
  if (p == NULL)
  {
    perror("calloc");
    return 1;
  }
  int i = 0;
  for (i = 0; i < 10; i++)
    printf("%d\n", *(p + i));
  return 0;
}

🎊realloc

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

有时候我们发现之前申请的内存过小了,有时候我们发现我们申请的内存过大了,所以,在一些时候,我们需要对内存的大小做灵活的调整。那么realloc函数就可以做到对动态开辟内存大小的调整

  • realloc函数的原型如下:
void* realloc(void* ptr,size_t size);

其中,ptr是要调整的内存的地址

size是调整之后的新的内存的大小。

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

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

举个例子:

我们首先使用malloc函数开辟40个字节的空间:

//malloc函数申请空间
int* p = (int*)malloc(40);
if (p == NULL)
{
  perror("malloc");
  return 1;
}
//初始化
int i = 0;
for (i = 0; i < 10; i++)
  *(p + i) = i;

我们想要将这个空间扩大,扩大为80个字节的空间。

于是我们使用realloc函数进行调整。但是内存空间的变化可能有不同的情况。

情况一,后面有足够的空间。

即这样的情形:

情况二,后面没有足够的空间:

//增加一些空间
int *ptr=realloc(p, 80);
if (ptr != NULL)
{
  p = ptr;
}

在用realloc函数调整动态内存空间时,要注意不能将原来的p指针,来接收realloc(p,80)。

这是因为relloc函数不一定开辟成功新的空间而进行调整,即realloc函数的返回值可能为NULL。

那么p=NULL,本来p维护40个字节的空间。那么这样那个40个字节空间的字节就没有指针维护了。但还没有释放,可能用不到了,但可能找不到了,从而造成内存泄露。

相关文章
|
11月前
|
安全 C语言 C++
比较C++的内存分配与管理方式new/delete与C语言中的malloc/realloc/calloc/free。
在实用性方面,C++的内存管理方式提供了面向对象的特性,它是处理构造和析构、需要类型安全和异常处理的首选方案。而C语言的内存管理函数适用于简单的内存分配,例如分配原始内存块或复杂性较低的数据结构,没有构造和析构的要求。当从C迁移到C++,或在C++中使用C代码时,了解两种内存管理方式的差异非常重要。
362 26
|
11月前
|
安全 C语言
C语言中的字符、字符串及内存操作函数详细讲解
通过这些函数的正确使用,可以有效管理字符串和内存操作,它们是C语言编程中不可或缺的工具。
465 15
|
存储 缓存 算法
JVM简介—1.Java内存区域
本文详细介绍了Java虚拟机运行时数据区的各个方面,包括其定义、类型(如程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和直接内存)及其作用。文中还探讨了各版本内存区域的变化、直接内存的使用、从线程角度分析Java内存区域、堆与栈的区别、对象创建步骤、对象内存布局及访问定位,并通过实例说明了常见内存溢出问题的原因和表现形式。这些内容帮助开发者深入理解Java内存管理机制,优化应用程序性能并解决潜在的内存问题。
755 29
JVM简介—1.Java内存区域
|
缓存 监控 算法
JVM简介—2.垃圾回收器和内存分配策略
本文介绍了Java垃圾回收机制的多个方面,包括垃圾回收概述、对象存活判断、引用类型介绍、垃圾收集算法、垃圾收集器设计、具体垃圾回收器详情、Stop The World现象、内存分配与回收策略、新生代配置演示、内存泄漏和溢出问题以及JDK提供的相关工具。
JVM简介—2.垃圾回收器和内存分配策略
|
存储 编解码 安全
阿里云高性能企业级甄选Intel第八代计算型c8i、通用型g8i和内存型r8i实例简介
计算型c8i、通用型g8i和内存型r8i实例是阿里云推出的高性能企业级甄选Intel第八代云服务器实例,采用CIPU+飞天技术架构,搭载最新的Intel 第五代至强可扩展处理器(代号EMR),性能进一步大幅提升,同时拥有AMX加持的AI能力增强,并在全球范围率先支持TDX机密虚拟机能力,实现了AI增强和全面安全防护的两大特色优势。本文将为您介绍这三个实例规格的性能、适用场景及最新活动价格以及选择指南,以供选择参考。
604 18
|
存储 缓存 算法
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
610 6
|
11月前
|
存储
阿里云轻量应用服务器收费标准价格表:200Mbps带宽、CPU内存及存储配置详解
阿里云香港轻量应用服务器,200Mbps带宽,免备案,支持多IP及国际线路,月租25元起,年付享8.5折优惠,适用于网站、应用等多种场景。
3234 0
|
11月前
|
存储 缓存 NoSQL
内存管理基础:数据结构的存储方式
数据结构在内存中的存储方式主要包括连续存储、链式存储、索引存储和散列存储。连续存储如数组,数据元素按顺序连续存放,访问速度快但扩展性差;链式存储如链表,通过指针连接分散的节点,便于插入删除但访问效率低;索引存储通过索引表提高查找效率,常用于数据库系统;散列存储如哈希表,通过哈希函数实现快速存取,但需处理冲突。不同场景下应根据访问模式、数据规模和操作频率选择合适的存储结构,甚至结合多种方式以达到最优性能。掌握这些存储机制是构建高效程序和理解高级数据结构的基础。
1057 1