C语言之动态内存管理(malloc calloc realloc)

2023-12-08 15:52

本文主要是介绍C语言之动态内存管理(malloc calloc realloc),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

C语言之动态内存管理

文章目录

  • C语言之动态内存管理
    • 1. 为什么要有动态内存管理
    • 2. malloc 和 free
      • 2.1 malloc
      • 2.2 free
      • 2.3 例子
    • 3. calloc 和 realloc
      • 3.1 calloc
      • 3.2 realloc
    • 4. 常见的动态内存错误
      • 4.1 对NULL指针的解引⽤操作
      • 4.2 对动态开辟空间的越界访问
      • 4.3 对⾮动态开辟内存使⽤free释放
      • 4.4 使⽤free释放⼀块动态开辟内存的⼀部分
      • 4.5 对同⼀块动态内存多次释放
      • 4.6 动态开辟内存忘记释放(内存泄漏)
    • 5. 总结

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

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

#include <stdio.h>int main()
{int val = 20;int arr[10] = { 0 };return 0;
}

上述的开辟空间的⽅式有两个特点
• 空间开辟大小是固定的

•数组在申明的时候,必须指定数组的⻓度,数组空间⼀旦确定了⼤⼩不能调整

所以C语⾔引⼊了动态内存开辟,让程序员⾃⼰可以申请和释放空间

2. malloc 和 free

mallocfree函数都是在stdlib.h头文件中声明的

2.1 malloc

C语言中提供了一个动态内存开辟的函数:

void* malloc (size_t size);

其中size为要开辟的内存空间的大小,单位为字节

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

•如果开辟成功,则返回一个指向开辟好的内存空间的指针
•如果开辟失败,则返回一个NULL指针
•返回类型为void *,因为malloc函数不知道要开辟什么类型的内存空间,只知道要开辟的大小
•如果参数为0,malloc函数的行为标准是未定义的,取决于编译器

2.2 free

C语言还提供了一个的函数,专门用来做动态内存的释放和回收的:

void free (void* ptr);

ptr为要释放内存空间的指针

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

如果不对malloc calloc realloc 开辟的空间进行释放,即使出了作用域也不会销毁,有可能导致内存泄漏

释放的方式
1. free
2. 直到程序结束,由操作系统释放

2.3 例子

#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{int* p = (int*)malloc(10 * sizeof(int)); //开辟40个字节的空间//判断是否为NULL指针if (p == NULL){perror("malloc fail\n"); //perror为错误信息打印return 1;}//使用int i = 0;for (i = 0; i < 10; i++){*(p + i) = i;//*p = i;   //如果使用这种方法,p指针向后走了,在下面打印时,就找不到首元素的地址了//p++;}//打印for (i = 0; i < 10; i++){printf("%d ", *(p + i));}free(p);  //释放p = NULL; //将指针置NULL,如果不置NULL,下面解引用p时,p就是野指针return 0;
}

代码运行结果:>
0 1 2 3 4 5 6 7 8 9

malloc开辟空间时,是不会给空间初始化的,如果直接打印,会打印出随机值

3. calloc 和 realloc

3.1 calloc

C语⾔还提供了⼀个函数叫 calloccalloc 函数也⽤来动态内存分配

void* calloc (size_t num, size_t size);

num为要开辟的元素个数
size为开辟元素的元素大小,单位为字节

calloc为开辟num个大小为size元素的内存空间,并且将内存中每个字节初始化为0
calloc的使用方法和malloc一样,主要区别在于calloc会初始化元素

例子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{int* p = (int*)calloc(10 ,sizeof(int));//判断是否为NULL指针if (p == NULL){perror("calloc fail\n");return 1;}int i = 0;for (i = 0; i < 10; i++){printf("%d ", *(p + i));}return 0;
}

代码运行结果:>
0 0 0 0 0 0 0 0 0 0

3.2 realloc

C语言中有一个函数用来调整动态内存开辟后的大小

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

ptr为要调整的内存地址
size为调整后的内存大小

realloc函数的出现让动态内存管理更加灵活
• 当我们发现我们使用malloc calloc realloc申请的内存空间不够时,我们可以使用realloc进行扩容

•返回值为调整之后的内存的起始位置(不一定是原内存地址)
•如果开辟失败则返回一个NULL
•如果开辟成功则分以下两个情况:

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

在这里插入图片描述

情况1:在原有内存后边直接追加空间,原来的空间的数据不变
情况2:原有内存之后的空间不足以最加空间,那么realloc会在堆区中找到一块足够开辟新大小的空间,将旧空间中的数据拷贝到新空间,并且将旧空间释放,同时返回新空间起始位置的地址

realloc的用法除了为开辟的内存进行扩容,也可以和malloc一样

例子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{int* p = (int*)malloc(10 * sizeof(int));//判断是否为NULL指针if (p == NULL){perror("malloc fail\n");return 1;}int* tmp = (int*)realloc(p, 100*sizeof(int));if (tmp != NULL){p = tmp;}else{perror("relloc fail\n");return 1;}//使用//..........free(p);p = NULL;return 0;
}

在这里插入图片描述
这次的运行结果就是情况2(开辟100个字节的大小时,可能会出现),当后面的空间不够时, realloc就会找一块新的空间
在这里插入图片描述
这次只开辟了40个字节的空间,属于情况1,后面的空间足够时, realloc会直接在后面追加空间

4. 常见的动态内存错误

4.1 对NULL指针的解引⽤操作

#include <stdio.h>
#include <stdlib.h>int main()
{int* p = (int*)malloc(40);*p = 20; //如果malloc开辟空间失败,p可能是NULL,此时p为野指针return 0;
}

在VS2022中,编译器会进行提示,我们得对可能出现NULL的情况进行处理
在这里插入图片描述

在使用malloc calloc realloc开辟空间时,最好对返回值进行判断,当不为NULL再使用

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{int* p = (int*)malloc(40);if (p == NULL){perror("malloc fail\n");return 1;}int i = 0;for (i = 0; i <= 10; i++)  //只有10个元素的空间,却访问了第11个元素,访问越界了{*(p + i) = i;}free(p);p = NULL;return 0;
}

在这里插入图片描述

4.3 对⾮动态开辟内存使⽤free释放

#include <stdio.h>
#include <stdlib.h>int main()
{int a = 10;int* p = &a;free(p);return 0;
}

在这里插入图片描述

当用free释放了不是由malloc calloc realloc开辟的空间时,就会报错

4.4 使⽤free释放⼀块动态开辟内存的⼀部分

#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{int a = 10;int* p = (int*)malloc(40);if (p == NULL){perror("malloc fail\n");return 1;}//使用//......p++;free(p);p = NULL;return 0;
}

在这里插入图片描述
当用free释放了开辟空间的一部分时,就会报错

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{int a = 10;int* p = (int*)malloc(40);if (p == NULL){perror("malloc fail\n");return 1;}//使用//......free(p);free(p);p = NULL;return 0;
}

在这里插入图片描述
对一块动态开辟的内存进行多次free释放

在上述代码中如果free释放NULL,则没有问题,因为free的参数为NULL时,则什么都不做

#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{int a = 10;int* p = (int*)malloc(40);if (p == NULL){perror("malloc fail\n");return 1;}//使用//......free(p);p = NULL;free(p);p = NULL;return 0;
}

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

#include <stdio.h>
#include <stdlib.h>void test()
{int* p = (int*)malloc(100);if (p != NULL){*p = 20;}
}
int main()
{test();while (1); //死循环,让程序不结束return 0;
}

当动态开辟的内存不释放时,就会一存在,在上述代码中,调用了test函数,开辟了100个字节的空间,同时赋值,出函数时,p被销毁了,但是开辟的空间并没有被销毁,没人可以使用,也没人可以释放,就会导致内存泄漏

5. 总结

一丶
在使用malloc calloc realloc开辟的空间时,要对其进行判断,当不为NULL的再进行使用
二丶
当不使用动态开辟的内存时,将其free释放,同时将指针置NULL,防止可能出现的内存泄露和野指针
三丶
不对不是动态开辟的空间free,不连续对动态开辟的空间free,同时free动态开辟的空间时,要给开辟的起始地址,不能free部分空间

这篇关于C语言之动态内存管理(malloc calloc realloc)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/470451

相关文章

python uv包管理小结

《pythonuv包管理小结》uv是一个高性能的Python包管理工具,它不仅能够高效地处理包管理和依赖解析,还提供了对Python版本管理的支持,本文主要介绍了pythonuv包管理小结,具有一... 目录安装 uv使用 uv 管理 python 版本安装指定版本的 Python查看已安装的 Python

Go 语言中的select语句详解及工作原理

《Go语言中的select语句详解及工作原理》在Go语言中,select语句是用于处理多个通道(channel)操作的一种控制结构,它类似于switch语句,本文给大家介绍Go语言中的select语... 目录Go 语言中的 select 是做什么的基本功能语法工作原理示例示例 1:监听多个通道示例 2:带

基于Python和MoviePy实现照片管理和视频合成工具

《基于Python和MoviePy实现照片管理和视频合成工具》在这篇博客中,我们将详细剖析一个基于Python的图形界面应用程序,该程序使用wxPython构建用户界面,并结合MoviePy、Pill... 目录引言项目概述代码结构分析1. 导入和依赖2. 主类:PhotoManager初始化方法:__in

C语言函数递归实际应用举例详解

《C语言函数递归实际应用举例详解》程序调用自身的编程技巧称为递归,递归做为一种算法在程序设计语言中广泛应用,:本文主要介绍C语言函数递归实际应用举例的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录前言一、递归的概念与思想二、递归的限制条件 三、递归的实际应用举例(一)求 n 的阶乘(二)顺序打印

C语言中的数据类型强制转换

《C语言中的数据类型强制转换》:本文主要介绍C语言中的数据类型强制转换方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C语言数据类型强制转换自动转换强制转换类型总结C语言数据类型强制转换强制类型转换:是通过类型转换运算来实现的,主要的数据类型转换分为自动转换

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件

C语言实现两个变量值交换的三种方式

《C语言实现两个变量值交换的三种方式》两个变量值的交换是编程中最常见的问题之一,以下将介绍三种变量的交换方式,其中第一种方式是最常用也是最实用的,后两种方式一般只在特殊限制下使用,需要的朋友可以参考下... 目录1.使用临时变量(推荐)2.相加和相减的方式(值较大时可能丢失数据)3.按位异或运算1.使用临时

使用C语言实现交换整数的奇数位和偶数位

《使用C语言实现交换整数的奇数位和偶数位》在C语言中,要交换一个整数的二进制位中的奇数位和偶数位,重点需要理解位操作,当我们谈论二进制位的奇数位和偶数位时,我们是指从右到左数的位置,本文给大家介绍了使... 目录一、问题描述二、解决思路三、函数实现四、宏实现五、总结一、问题描述使用C语言代码实现:将一个整

nvm如何切换与管理node版本

《nvm如何切换与管理node版本》:本文主要介绍nvm如何切换与管理node版本问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录nvm切换与管理node版本nvm安装nvm常用命令总结nvm切换与管理node版本nvm适用于多项目同时开发,然后项目适配no

C语言字符函数和字符串函数示例详解

《C语言字符函数和字符串函数示例详解》本文详细介绍了C语言中字符分类函数、字符转换函数及字符串操作函数的使用方法,并通过示例代码展示了如何实现这些功能,通过这些内容,读者可以深入理解并掌握C语言中的字... 目录一、字符分类函数二、字符转换函数三、strlen的使用和模拟实现3.1strlen函数3.2st