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

文章目录

  • 写在前面
  • 1. malloc 和 free函数
    • 1.1 malloc函数介绍
    • 1.2 free函数介绍
  • 2. calloc函数
  • 3. realloc函数
  • 4. 常见的动态内存错误
    • 4.1 对NULL指针的解引用操作
    • 4.2 对动态开辟空间的越界访问
    • 4.3 对非动态开辟内存使用free释放
    • 4.4 使用free释放一块动态开辟内存的一部分
    • 4.5 对同一块动态内存多次释放
    • 4.6 动态开辟内存忘记释放(内存泄漏)

写在前面

本文介绍了C语言中四个常用的内存分配和释放函数,它们是:malloc、calloc、realloc 、free。文章中讨论了这些函数的使用方法,示例代码,常见的动态内存错误以及如何防止内存泄漏。

1. malloc 和 free函数

malloc和free是C语言中用于动态内存分配和释放的函数,它俩基本上都是配套使用的。malloc和free的声明在 stdlib.h 头文件中。

1.1 malloc函数介绍

函数原型:

void* malloc (size_t size);
  • 函数参数:size表示要申请的内存大小,单位是字节。如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器
  • 函数返回值:返回一个void*类型的指针,这是因为malloc函数并不知道开辟空间的类型,所以在使用的时候由使用者自己来决定。如果开辟成功,则返回动态开辟空间的起始地址;如果开辟失败,返回空指针(NULL),因此对malloc的返回值一定要做检查

以下代码,演示了如何使用malloc函数来动态申请内存并在其中存储整数数据:

#include <stdio.h>
#include <stdlib.h>int main() 
{int* arr;int size = 5;// 使用malloc分配整数数组的内存arr = (int*)malloc(size * sizeof(int));//对malloc的返回值进行检查if (arr == NULL) {perror("malloc");return 1;}// 在动态数组中存储数据for (int i = 0; i < size; i++) {arr[i] = i;}// 使用动态数组中的数据for (int i = 0; i < size; i++){printf("%d ", arr[i]);}// 释放动态分配的内存free(arr);arr = NULL;return 0;
}

需要注意的是
使用malloc函数动态申请的空间在不需要使用的时候要用free函数释放,以避免内存泄漏。释放内存后,应将指针设置为NULL,以避免出现野指针的问题。

1.2 free函数介绍

函数原型:

void free (void* ptr);
  • 函数参数:ptr是要释放的内存空间起始地址。

free函数主要是用来回收内存的,用于释放先前通过动态内存函数(例如malloc、calloc、realloc等)申请的空间。

需要注意的是:

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

以下代码,演示了如何使用free函数来释放动态申请的内存:

#include <stdio.h>
#include <stdlib.h>int main()
{int* arr;int size = 5;// 使用malloc分配整数数组的内存arr = (int*)malloc(size * sizeof(int));//对malloc的返回值进行检查if (arr == NULL){perror("malloc");return 1;}// 使用内存// ....// 释放动态分配的内存free(arr);arr = NULL;return 0;
}

2. calloc函数

函数原型:

void* calloc (size_t num, size_t size);
  • num:表示要分配的元素的数量。
  • size:表示每个元素的大小(以字节为单位)。
  • 返回值:如果开辟成功,则返回动态开辟空间的起始地址;如果开辟失败,返回空指针(NULL),因此对calloc的返回值也要做检查。

表面上看calloc和malloc函数的功能非常相似,但它与malloc也是有区别的。区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0
在这里插入图片描述
以下是上面图片中的代码:

#include <stdio.h>
#include <stdlib.h>int main()
{int* ptr;// 使用calloc申请空间ptr = (int*)calloc(5, sizeof(int));//对calloc的返回值进行检查if (ptr == NULL){perror("malloc");return 1;}// 使用内存// ....// 释放动态分配的内存free(ptr);ptr = NULL;return 0;
}

需要注意的是:

  • 和malloc类似,使用calloc函数动态申请的空间在不需要使用的时候也需要用free函数释放,以避免内存泄漏。释放内存后,也应将指针设置为NULL,以避免出现野指针的问题。
  • 如果我们对申请的内存空间的内容要求初始化,可以使用calloc函数来完成任务。

3. realloc函数

函数原型:

void* realloc (void* ptr, size_t size);
  • ptr:是先前分配的内存区域的起始地址。
  • size:是重新分配后的内存块的新大小,单位是字节。
  • 返回值:如果调整成功,则返回一个调整好的空间的起始地址;如果调整失败,返回空指针(NULL),因此对realloc的返回值一定要做检查。

realloc 函数是用来调整动态开辟的内存大小的。realloc在调整内存空间是存在两种情况:
情况1:原有空间之后有足够大的空间
在这里插入图片描述
情况2:原有空间之后没有足够大的空间
当是情况2的时候,原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用。这样函数返回的是一个新的内存地址。
在这里插入图片描述
以下代码,首先使用malloc申请一块初始大小的内存,然后使用realloc调整初始内存。

#include <stdio.h>
#include <stdlib.h>int main() 
{int* arr;size_t sz = 5;size_t newSz = 10;// 分配内存arr = (int*)malloc(sz * sizeof(int));if (arr == NULL) {perror("malloc");return 1;}// 使用内存// 重新分配内存int* _arr = (int*)realloc(arr, newSz * sizeof(int));if (_arr == NULL) {perror("malloc");free(arr);return 1;} else{arr = _arr;}// 使用重新分配后的内存// 释放内存free(arr);arr = NULL;return 0;
}

4. 常见的动态内存错误

4.1 对NULL指针的解引用操作

对NULL指针进行解引用操作是一种非常严重的错误,因为NULL指针表示一个无效的内存地址,尝试访问它的内容将导致未定义的行为,通常会导致程序崩溃。
如下面代码,如果malloc开辟空间失败就会返回空指针,造成对NULL指针进行解引用。

#include <stdio.h>
#include <stdlib.h>void test()
{int *p = (int *)malloc(INT_MAX/4);*p = 20;//如果p的值是NULL,就会有问题free(p);
}int main()
{test();return 0;
}

代码改正:
检查指针是否为NULL:在进行指针解引用操作之前,应该先检查指针是否为NULL。

#include <stdio.h>
#include <stdlib.h>void test()
{int *p = (int *)malloc(INT_MAX/4);//解引用之间,检查指针是否为NULLif (p == NULL){perror("malloc");return;}*p = 20;//如果p的值是NULL,就会有问题free(p);p = NULL;
}int main()
{test();return 0;
}

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

对动态开辟空间的越界访问是指在使用malloc、calloc、realloc等动态内存分配函数分配的内存空间中,访问超出申请空间的内存位置。这是一种严重的编程错误,通常会导致不可预测的行为和程序崩溃。

如下面代码,当 i 是10的时候越界访问。

#include <stdio.h>
#include <stdlib.h>void test()
{int i = 0;int *p = (int *)malloc(10*sizeof(int));if(NULL == p){perror("malloc");return;}for(i=0; i<=10; i++){*(p+i) = i;//当i是10的时候越界访问}free(p);p = NULL;
}int main()
{test();return 0;
}

因此,在使用指针进行操作时,一定要确保我们了解分配内存的大小,以避免越界访问

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

使用 free 函数释放非动态分配的内存是一种非常严重的错误。free 函数应该仅用于释放通过 malloc、calloc、realloc 等动态内存分配函数申请的空间。如果尝试使用 free 来释放不是动态分配的内存,将导致不可预测的行为,通常会导致程序崩溃。

如下代码,试图使用 free 释放栈上局部变量,这将导致程序崩溃。

void test()
{int a = 10;int *p = &a;free(p);//ok?
}
int main()
{test();return 0;
}

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

free 函数通常用于释放整个动态申请的空间,而不是它的一部分。一旦我们使用 malloc、calloc 或 realloc 分配了内存,就只能使用 free 来释放整个块。如果使用free释放一块动态开辟内存的一部分,通常会导致程序崩溃。

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

多次释放同一块动态内存是一种非常严重的错误,通常会导致不可预测的行为和程序崩溃。这是因为一旦申请的空间被释放,它就不再属于当前的程序,再次释放它将导致内存错误。
如何避免同一空间被重复释放?
我们可以在每次释放后将指针设为NULL:一旦我们使用 free 函数释放了动态分配的内存,将指针设为 NULL,后续在通过该指针进行内存释放的时候就不会出问题了,这是因为当free函数的参数 是NULL指针时,函数什么事都不做。

	int* ptr = (int*)malloc(sizeof(int));// ...free(ptr);ptr = NULL; // 将指针设为 NULL,避免再次释放

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

内存泄漏是指在程序运行过程中动态申请的的空间没有被释放,导致程序持续占用系统内存而不释放,最终可能导致系统内存资源被耗尽。由于忘记释放不再使用的动态开辟的空间会造成内存泄漏,因此,动态开辟的空间一定要释放,并且正确释放

有句说的好,一个人想写出来有bug的代码,是拦不住的。因此,我们在编程的时候,如果能避免这些动态内存错误,那么我们写出的代码将是高质量、稳定和可维护的代码!!!

至此,本片文章就结束了,若本篇内容对您有所帮助,请三连点赞,关注,收藏支持下。
创作不易,白嫖不好,各位的支持和认可,就是我创作的最大动力,我们下篇文章见!
如果本篇博客有任何错误,请批评指教,不胜感激 !!!
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/158391.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

低代码提速应用开发

低代码介绍 低代码平台是指一种能够帮助企业快速交付业务应用的平台。自2000年以来&#xff0c;低代码市场一直充斥着40大大小小的各种玩家&#xff0c;比如国外的Appian、K2、Pega Systems、Salesforce和Ultimus&#xff0c;国内的H3 BPM。 2015年以后&#xff0c;这个市场更是…

linux 安装python django pip 遇到的问题

Python解决SSL不可用问题 解决方案&#xff1a; 首先要明白python版本需要和openssl的版本需要相对匹配的&#xff0c;在Python3.7之后的版本&#xff0c;依赖的openssl&#xff0c;必须要是1.1或者1.0.2之后的版本&#xff0c;或者安装了2.6.4之后的libressl&#xff0c;linux…

centos7下安装elasticsearch7.8.1并配置远程连接

1、下载安装包 sudo wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.8.1-linux-x86_64.tar.gz 2、解压 sudo tar -zxvf elasticsearch-7.8.1-linux-x86_64.tar.gz 3、添加用户并设置密码 sudo useradd es sudo passwd es # 设置密码 Lida15…

Pycharm 2023 设置远程调试

pycharm 版本 &#xff1a; 2023.2.1 整体流程参考&#xff1a;https://blog.csdn.net/xuanhaolaile/article/details/128293254 首先确定远程服务器上已经安装好 requirements.txt 中所需的依赖包。 1、SSH Configurations 添加远程服务器 2、Python Interpreter 注意&…

Leetcode算法解析——查找总价格为目标值的两个商品

1. 题目链接&#xff1a;LCR 179. 查找总价格为目标值的两个商品 2. 题目描述&#xff1a; 商品价格按照升序记录于数组 price。请在购物车中找到两个商品的价格总和刚好是 target。若存在多种情况&#xff0c;返回任一结果即可。 示例 1&#xff1a; 输入&#xff1a;price …

Linux信号 signal()编程

在Linux的进程间通信中可以用signal&#xff08;&#xff09;函数进行信号与信息传递。 1.信号 信号的名字和编号&#xff1a; 每个信号都有一个名字和编号&#xff0c;这些名字都以“SIG”开头&#xff0c;例如“SIGIO ”、“SIGCHLD”等等。 信号定义在signal.h头文件中&am…

虚幻阴影整理

虚拟阴影贴图&#xff08;VSM&#xff09;是一种全新的阴影贴图方法&#xff0c;可以提供稳定的高分辨率阴影。通过与虚幻引擎5的Nanite虚拟几何体、Lumen全局光照和反射以及世界分区功能结合使用&#xff0c;它能够实现电影级的品质效果&#xff0c;为大型开放场景提供光照。 …

html和css基础练习

vscode快捷键 alt b 在浏览器中打开 alt shift b 在其他浏览器打开 ctrl / 注释 ctrl y 快捷键删除 参考文章 https://www.bilibili.com/video/BV1m84y1w7Tb 基础html标签 img&#xff1a;图像&#xff0c;title&#xff1a;头部文字&#xff0c;body&#xff1a;主…

使用匿名函数在Golang中的好处

发挥Golang中无名代码块的潜力 匿名函数&#xff0c;也被称为lambda函数或闭包&#xff0c;是Golang中的一个强大功能&#xff0c;提供了许多好处。这些无名代码块为开发人员在设计和构建其代码时提供了更大的灵活性和模块化。在本节中&#xff0c;我们将探讨使用匿名函数可以…

AI对网络安全的影响与挑战

近年来&#xff0c;随着人工智能&#xff08;AI&#xff09;技术的快速发展&#xff0c;网络安全领域也开始逐渐引入生成式AI应用。根据最新的数据研究&#xff0c;生成式AI对网络安全和合规的影响最大&#xff0c;同时也包括了IT和云的运维、硬件和软件支持领域。通过AI和自动…

基于Java的点歌管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

【VTK】基础知识分析

很高兴在雪易的CSDN遇见你 &#xff0c;给你糖糖 欢迎大家加入雪易社区-CSDN社区云 前言 本文分享VTK基础操作技术&#xff0c;记录vtk编程中常用的接口&#xff0c;变量等的创建及使用方法希望对各位小伙伴有所帮助&#xff01; 感谢各位小伙伴的点赞关注&#xff0c;小易…

华为云云耀云服务器L实例评测|华为云耀云服务器L实例私有库搭建verdaccio(八)

九、华为云耀云服务器L实例私有库搭建verdaccio&#xff1a; Verdaccio 是一个简单的、零配置本地私有 npm 软件包代理注册表。Verdaccio 开箱即用&#xff0c;拥有自己的小型数据库&#xff0c;能够代理其它注册表&#xff08;例如 npmjs.org&#xff09;&#xff0c;缓存下载…

软件工程与计算总结(十)软件体系结构设计与构建

目录 ​编辑 一.体系结构设计过程 1.分析关键需求和项目约束 2.选择体系结构风格 3.体系结构逻辑设计 4.体系结构实现 5.完善体系结构设计 6.定义构件接口 二.体系结构原型构建 1.包的创建 2.重要文件的创建 3.定义构件之间的接口 4.关键需求的实现 三.体系结构的…

屏幕亮度调节保护您的眼睛

官方下载地址&#xff1a; 安果移动 视频演示&#xff1a;屏幕亮度调节-保护您的眼睛_哔哩哔哩_bilibili 嗨&#xff0c;亲爱的用户&#xff0c;你是否有过这样的体验&#xff1a;夜晚安静的时刻&#xff0c;想要在抖音上看看热门的舞蹈、在快手上发现生活的 趣味、或是在哔…

Godot2D角色导航-自动寻路教程(Godot实现角色随鼠标移动)

文章目录 运行结果2D导航概述开始前的准备2D导航创建导航网格创建角色 其他文章 运行结果 2D导航概述 Godot为2D和3D游戏提供了多个对象、类和服务器&#xff0c;以便于基于网格或基于网格的导航和路径查找。 说到导航&#xff0c;就得说一下导航网格&#xff0c;导航网格定义…

区块链跨链技术

区块链跨链技术 背景 近年来&#xff0c;随着区块链技术的不断发展&#xff0c;区块链的应用场景逐渐从最初的加密货币领域扩展到金融、物流、医疗、公共服务等各个领域。随着区块链的应用场景不断增多&#xff0c;区块链的“数据孤岛”问题日益突出&#xff0c;不同场景下的…

发现更多美景!XnViewMP for Mac/Windows 图片浏览软件

想要轻松快捷地浏览、管理和编辑您的照片吗&#xff1f;XnViewMP for Mac 是您的最佳选择&#xff01;这款强大而多功能的图片浏览软件将给您带来全新的视觉体验。 借助 XnViewMP&#xff0c;您可以方便地浏览各种图片格式&#xff0c;包括JPEG、PNG、GIF等&#xff0c;并支持…

贴片电容材质的区别与电容的主要作用

一、贴片电容材质NPO、COG、X7R、X5R、Y5V、Z5U区别 主要是介质材料不同&#xff0c;不同介质种类由于它的主要极化类型不一样&#xff0c;其对电场变化的响应速度和极化率也不一样。在相同的体积下的容量就不同&#xff0c;随之带来的电容器介质的损耗、容量的稳定性也就不同…

Linux 文件系统逻辑结构图的解释

task_struct进程结构体&#xff0c;表示一个运行的进程。 task_struct中的fs指向fs_struct结构体。fs_struct表示这个进程支持的文件系统。 root指向根目录dentry&#xff0c;dentry中的d_inode指向改进程根目录在存储设备中的inode节点。 pwd指向当前进程所在的目录结构体den…