首页 > 系统相关 >深入理解动态内存(一):动态内存使用常见问题

深入理解动态内存(一):动态内存使用常见问题

时间:2024-09-07 11:22:14浏览次数:10  
标签:释放 常见问题 int free 深入 内存 动态内存 NULL 指针

目录

对NULL指针的解引用操作

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(INT_MAX / 4);//这里的INT_MAX是整形的最大值(只是为了满足malloc返回空指针)
	*p = 20;//如果p的值是NULL,就会有问题
	free(p);
	p = NULL;
	return 0;
}

当申请的空间太大导致堆区内存不够申请失败时,就会返回空指针。
C语言空指针的值为NULL。一般NULL指针指向进程的最小地址,通常这个值为0。试图通过空指针对数据进行访问,会导致运行时错误。当程序试图解引用一个期望非空但是实际为空的指针时,会发生空指针解引用错误。对空指针的解引用会导致未定义的行为。在很多平台上,解引用空指针可能会导致程序异常终止或拒绝服务。

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

void test()
{
	int i = 0;
	int* p = (int*)malloc(10 * sizeof(int));
	if (NULL == p)
	{
		exit(EXIT_FAILURE);
	}
	for (i = 0; i <= 10; i++)
	{
		*(p + i) = i;//当i是10的时候越界访问
	}
	free(p);
}
int main()
{
	test();
	return 0;
}

看一下运行结果:
在这里插入图片描述
运行时系统直接崩毁了。
原因:
申请了40个字节的空间,但是我们访问了44个,但是第41个字节开始的内存空间就不是我们所支配的空间,那这个时候,p就是野指针,对野指针的访问就会导致运行时系统崩溃。

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

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

在这里插入图片描述
当我们使用free释放p存放的地址空间时,系统又崩了。
原因:
我们在上一篇文章里面提到过,动态内存的开辟都是在堆区中进行的,那么free函数释放的空间也就是针对堆区的,但是我们自己创建的变量是在栈区或者静态区的,与free的可执行对象不一致,就会使系统崩溃。

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

void test()
{
	int* p = (int*)malloc(100);
	//代码1
	for (int i = 0; i < 10; i++)
	{
		p[i] = i;
		
	}
	//代码2
	for (int i = 0; i < 10; i++)
	{
		*p = i;
		p++;
	}

	free(p);//p不再指向动态内存的起始位置
}
int main()
{
	test();
	return 0;
}

我们申请了一块空间后在对这块空间进行使用,有的人利用指针进行操作习惯写第一段代码,这样没什么问题,因为p一直存放的都是动态空间的起始地址,但有的人会写成代码2的形式,每赋完一个值让地址加一在进行赋值,这样p就不再指向起始地址,用free释放就会使系统崩溃。

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

有的人在已经释放的前提下,因为中间一些事情打断了思路导致再次释放,这个其实没有那么严重的后果,因为上篇文章提到过对NULL释放free就不会有任何动作,但是会使得代码看起来奇怪啊,有的人看到就会很奇怪为什么要释放两次,会显得我们的逻辑比较差。

void test()
 {
 int *p = (int *)malloc(100);
 free(p);
 free(p);//重复释放
 }

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

这个使非常经典的错误了。

void test()
 {
 int *p = (int *)malloc(100);
 if(NULL != p)
 {
 *p = 20;
 }
 }
int main()
 {
 test();
 while(1);
 }

如果我们申请了一块地址,但是使用完后不及时释放,就会使堆区内存不足,导致后面再次申请动态内存失败,导致数据丢失等后果。
举个例子:
腾讯等一些大型公司服务器都是24*7小时的运行,加入有一段甚至几段代码循环执行而且每次都不释放,那这么长时间下来堆区的空间是否能一直够用,如果不够用就会使得下次的内存申请失败,导致数据丢失系统崩溃的后果。

总结:
忘记释放不再使⽤的动态开辟的空间会造成内存泄漏。
切记:动态开辟的空间⼀定要释放,并且正确释放。
----------------------------------------------分隔符
本次的介绍到此结束,感谢各位观看,有错请在评论区指正,谢谢!

标签:释放,常见问题,int,free,深入,内存,动态内存,NULL,指针
From: https://blog.csdn.net/qq_71391318/article/details/141991863

相关文章

  • 深入浅出孪生神经网络,高效训练模型
    大家好,在深度学习领域,神经网络几乎能处理各种任务,但通常需要依赖于海量数据来达到最佳效果。然而,对于像面部识别和签名验证这类任务,我们不可能总是有大量的数据可用。由此产生了一种新型的神经网络架构,称为孪生网络。孪生神经网络能够基于少量数据实现精准预测,本文将介绍孪生......
  • Java 线程池:参数、配置和常见问题以及案例示范
    Java线程池:参数、配置和常见问题以及案例示范线程池是提高系统性能和资源利用率的关键组件之一。通过合理配置线程池,可以有效地管理线程资源,避免系统过载,提升并发处理能力。本文将以电商交易系统为案例,详细讲解Java线程池的参数、配置、以及常见问题和解决方案以及在spr......
  • Android 开发避坑经验(2):深入理解Fragment与Activity交互
    在Android开发过程中,Fragment和Activity之间的交互是一个常见的难题,处理不当会引发UI更新问题、生命周期混乱、数据丢失等问题。这篇文章将深入探讨如何避免这些常见坑点,提供可靠的解决方案,并通过示例代码展示最佳实践。1.坑点:Fragment和Activity的生命周期差异......
  • MySQL 日期函数语法介绍和案例示范以及常见问题解决
    本文将以电商交易系统为例,详细讲解MySQL日期类型及其转化,常用的日期函数,以及一些解决常见问题的方案。一、MySQL日期数据类型MySQL提供了多种日期数据类型,适用于不同的使用场景。常见的日期类型包括DATE、DATETIME、TIMESTAMP、TIME和YEAR。DATE:只存储日期,不包含......
  • JavaScript学习文档(14):深入对象、内置构造函数、综合案例
    目录一、深入对象1、创建对象三种方式2、构造函数(1)构造函数(2)说明:(3)利用构造函数创建多个对象(4)实例化执行过程3、实例成员和静态成员(1)实例成员:(2)静态成员:二、内置构造函数1、Object2、Array(1)数组常见实例方法-核心方法(2)员工涨薪计算成本案例(3)还有些数组常见方法(4......
  • 识别并应对动态归纳类算法题:深入剖析与实战指南
    《识别并应对动态归纳类算法题:深入剖析与实战指南》在编程的世界里,算法题犹如一座座充满挑战的山峰,等待着开发者们去攀登。其中,动态归纳类算法题因其复杂性和灵活性,常常成为开发者们进阶路上的一道难关。本文将深入探讨如何识别并应对动态归纳类算法题,为大家提供一份全面的......
  • 深入探讨 MyBatis-Plus 的 LambdaQueryWrapper (方法使用大全+案例)
    个人名片......
  • C语言-第七章:字符和字符串函数、动态内存分配
    传送门:C语言-第六章-加餐:其他自定义类型目录第一节:字符和字符串函数    1-1.strlen函数和sizeof关键字    1-2.memcpy内存拷贝函数    1-3.memmove内存拷贝函数    1-4.memset内存设置函数    1-5.strtok字符串切割函数......
  • 深入解析CJS与MJS的差异:模块化编程中的两种主流模式比较
    在现代JaScript开发中,模块化编程已成为构建复杂应用的重要方式。常见的模块化标准有两种:CommonJS(CJS)和ESModule(MJS)。这两者在本质上虽然都是为了解决模块化问题,但在实现方式、使用场景等方面存在显著差异。本文将深入解析CJS与MJS的差异,帮助大家更好地理解它们的特点及在实际开发......
  • 深入探讨Spring中Bean的初始化方式
    一、Spring中的Bean定义与生命周期在探讨具体的初始化方式之前,有必要对Spring中的Bean定义与生命周期进行简单回顾。1.1Bean的定义在Spring中,Bean指的是由Spring容器管理的对象。Bean的定义可以通过以下几种方式:XML配置:通过<bean>标签定义Bean。Java配置:通过@Configura......