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

news2024/11/26 20:49:31

文章目录

  • 写在前面
  • 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);
	//解引用之间,检查指针是否为NULL
	if (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.coloradmin.cn/o/1090065.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Windows 移动设备管理

Windows 设备管理是指一组流程和工具&#xff0c;可帮助 IT 管理员简化企业中使用的Windows 设备的管理。管理企业中使用的 Windows 设备最好通过实施Windows MDM 解决方案来完成&#xff0c;以从单个控制台保护、管理和监视这些设备。Windows移动设备管理 &#xff08;MDM&…

CakePHP 3.x/4.x反序列化RCE链

最近网上公开了cakephp一些反序列化链的细节&#xff0c;但是没有公开poc&#xff0c;并且网上关于cakephp的反序列化链比较少&#xff0c;于是自己跟一下 &#xff0c;构造pop链。 CakePHP简介 CakePHP是一个运用了诸如ActiveRecord、Association Data Mapping、Front Contr…

美团Leaf使用

简介 在复杂分布式系统中&#xff0c;往往需要对大量的数据和消息进行唯一标识。如在美团点评的金融、支付、餐饮、酒店、猫眼电影等产品的系统中&#xff0c;数据日渐增长&#xff0c;对数据分库分表后需要有一个唯一ID来标识一条数据或消息&#xff0c;数据库的自增ID显然不…

全面解析找不到msvcr100.dll的解决方法,快速修复系统msvcr100.dll丢失问题!

在计算机的使用过程中&#xff0c;我们可能会遇到各种问题&#xff0c;其中之一就是“msvcr100.dll丢失”的问题。这个问题通常出现在运行某些程序或游戏时&#xff0c;提示找不到msvcr100.dll文件。这个文件是Microsoft Visual C 2010 Redistributable Package的一部分&#x…

学习 MPP 与 SMP 的区别,终于有人讲明白了

文章目录 01 SMPSMP 的典型特征如下&#xff1a;SMP 的缺点如下。 02 分布式MPP计算架构MPP 架构核心原理如下。 导读&#xff1a;当今数据计算领域主要的应用程序和模型可大致分为在线事务处理&#xff08;On-line Transaction Processing &#xff0c;OLTP&#xff09;、决策…

山西电力市场日前价格预测【2023-10-14】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-10-14&#xff09;山西电力市场全天平均日前电价为221.03元/MWh。其中&#xff0c;最高日前电价为341.15元/MWh&#xff0c;预计出现在18: 45。最低日前电价为0.00元/MWh&#xff0c;预计出…

招投标系统软件源码,招投标全流程在线化管理

功能描述 1、门户管理&#xff1a;所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含&#xff1a;招标公告、非招标公告、系统通知、政策法规。 2、立项管理&#xff1a;企业用户可对需要采购的项目进行立项申请&#xff0c;并提交审批&#xff0c;查看所…

lv8 嵌入式开发-网络编程开发 17 套接字属性设置

1 基本概念 设置套接字的选项对套接字进行控制除了设置选项外&#xff0c;还可以获取选项选项的概念相当于属性&#xff0c;所以套接字选项也可说是套接字属性有些选项&#xff08;属性&#xff09;只可获取&#xff0c;不可设置&#xff1b;有些选项既可设置也可获取 2 选项…

Android组件通信——消息机制(二十六)

1. 消息机制 1.1 知识点 &#xff08;1&#xff09;掌握Message、Handler、Looper类的使用以及消息的传递&#xff1b; &#xff08;2&#xff09;可以通过消息机制动态取得信息&#xff1b; 1.2 具体内容 对于android的消息机制&#xff0c;我们主要要使用Java中线程的一…

医生访问学者出国进修必备面试技巧

医生访问学者出国进修&#xff0c;一直以来都是医学领域内追求更高学术水平和国际化视野的重要途径之一。然而&#xff0c;要成功进入国外院校或研究机构进行进修&#xff0c;首先需要通过面试&#xff0c;因此&#xff0c;面试技巧显得尤为关键。本文知识人网小编将为您介绍一…

LeetCode 739 每日温度(单调栈的初步了解)

1、重新学习了栈的操作&#xff0c;isEmpty()、peek()以及pop()、push()操作 但是值得注意的点是push()必须要有输入 2、单调栈用在这里非常巧妙&#xff0c;通过暴力搜索的方法无法通过最后一个用例 并且通过使用单调栈可以使得时间复杂度从O()降到了O() 3、Deque<Inte…

竞赛 深度学习+opencv+python实现车道线检测 - 自动驾驶

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络3.1卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 YOLOV56 数据集处理7 模型训练8 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &am…

回归预测 | MATLAB实现CNN-LSSVM基于卷积神经网络-最小二乘支持向量机的数据回归预测(多指标,多图)

回归预测 | MATLAB实现CNN-LSSVM基于卷积神经网络-最小二乘支持向量机的数据回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现CNN-LSSVM基于卷积神经网络-最小二乘支持向量机的数据回归预测&#xff08;多指标&#xff0c;多图&#xff09;…

关于信息安全软考的记录6

1、入侵检测相关概念 及 入侵检测模型 入侵&#xff1a;违背访问目标的安全策略的行为 判断入侵的依据是&#xff1a;对目标的操作是否超出了目标的安全策略范围 入侵检测&#xff1a;通过收集操作系统、系统程序、应用程序、网络包等信息&#xff0c;发现系统中违背安全策略…

【unity】【VR】白马VR课堂系列-VR开发核心基础04-主体设置-XR Rig的引入和设置

接下来我们开始引入并构建XR Rig。 你可以将XR Rig理解为玩家在VR世界中的替身。 我们先删除Main Camera&#xff0c;在Hierarchy右键点击删除。 然后再在场景层右键选择XR下的XR Origin。这时一个XR Origin对象就被添加到了Hierarchy。 重设XR Origin的Position和Rotation…

C++中将十六进制数转化为字符串数据

C中将十六进制数转化为字符串数据 1、十六进制转字符串2、string转char[]3、调用4、调试结果 1、十六进制转字符串 std::string Number2HexStr( uint32_t mData ) {std::stringstream ss;ss << std::hex << std::setw(2) << std::setfill(0) << (int)…

快速排序 O(nlgn)

大家好&#xff0c;我是蓝胖子&#xff0c;我一直相信编程是一门实践性的技术&#xff0c;其中算法也不例外&#xff0c;初学者可能往往对它可望而不可及&#xff0c;觉得很难&#xff0c;学了又忘&#xff0c;忘其实是由于没有真正搞懂算法的应用场景&#xff0c;所以我准备出…

el-dialog两个弹框里面套弹框受外层弹框影响

el-dialog嵌套的影响及解决方法 解决方法如下&#xff1a; 在里层弹框里添加 append-to-body <el-dialogtitle"图片预览":visible.sync"dialogVisible"class"imgDialog":modal"false"append-to-body><img width"100%&q…

分享一个查询OpenAI Chatgpt key余额查询的工具网站

OpenAI Key 余额查询工具 欢迎使用 OpenAI Key 余额查询工具网站&#xff01;这个工具可以帮助您轻松地验证您的 OpenAI API 密钥&#xff0c;并查看您的余额。 http://tools.lbbit.top/check_key/ 什么是 OpenAI Key 余额查询工具&#xff1f; OpenAI Key 余额查询工具是一…

要想成为黑客,离不开这些资料

目录 一、想入门学黑客&#xff0c;去哪里找详细的教程&#xff1f; 二、适合新人入门的书籍 三、相关网站推荐 四、在线靶场 五、Web安全学习路线 六、Web安全入门基础学习 小白在学习黑客的过程中一般会遇到这样一些问题&#xff1a;感觉自己工具、原理都会了但是遇到真…