第五十四天学习记录:C语言进阶:动态内存管理Ⅱ

news2024/11/26 16:29:57

常见的动态内存错误

1、对NULL指针的解引用操作

int* p=(int*)malloc(4);
//p进行相关的判断
*p=10;//malloc开辟空间失败,有可能对NULL指针解引用
free(p);
p=NULL;

2、对动态开辟的内存的越界访问

int* p=(int*)malloc(40);//10个int
if(p!=NULL)
{
	int i=0;
	//越界
	for(i=0;i<=10;i++)
	{
		*(p+i)=i;
	}
	free(p);
	p=NULL;
}

3、对非动态开辟内存的free

int a=10;
int* p=&a;
//...
//对非动态开辟内存的free
free(p);

4、使用free释放动态开辟内存的一部分

int* p=(int*)malloc(40);
if(p!=NULL)
{
	int i=0;
	for(i=0;i<10;i++)
	{
		*p++ = i;
	}
	free(p);
	p=NULL;
}

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

int* p=(int*)malloc(40);
if(p!=NULL)
{
	//使用
	free(p);
	//...
	free(p);
}

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

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int i = 0;
	while (1)
	{
		++i;
		printf("%d\n", i);
		malloc(1);
	}
	return 0;//死机警告!!!不要轻易尝试
}

问:realloc的第一个参数的指针地址必须是malloc或calloc创建的在堆上的地址吗?
ChatAI答:
是的,realloc 的第一个参数必须是由 malloccallocrealloc 函数返回的指针,且该指针必须指向堆上申请的内存空间。如果该指针不合法,也就是不是由上述函数返回的指针,或者指向的不是堆上的内存空间,那么 realloc 可能会导致未定义的行为。

问:realloc的第一个参数的指针也可以为NULL
ChatAI答:
是的,realloc 函数的第一个参数也可以是 NULL 指针。如果第一个参数是 NULL,那么 realloc 的行为和 malloc 的行为是等价的,如果分配内存成功,realloc 将会返回一个指向新内存块的指针。

例如下面的代码可以在堆上分配一段初始化为 0 的内存空间:

int *p = NULL;
p = realloc(p, sizeof(int) * 10);
if (p == NULL) {
    // 分配内存失败的处理
    printf("realloc failed");
} else {
    // 分配内存成功的处理,继续使用 p 指向的内存空间
    printf("realloc succeeded");
}

在函数调用后,如果 realloc 成功了,那么 p 将会指向一个大小为 10 个整数的内存块,并且内存块中的每个整数都将会被初始化为 0。如果 realloc 调用失败了,那么 p 会继续指向 NULL,程序会打印 “realloc failed”。

问:多次free(NULL);也不会报错吗?
ChatAI答:
在 C 语言中,多次调用free(NULL)不会导致程序崩溃或错误。因为在 C 标准中,对 NULL 指针调用 free 函数是被允许的,且不会影响程序的执行。

值得注意的是,在多次释放同一个内存指针时,可能会导致不可预测的错误,这应该避免这种情况的发生,通常情况下在free 之后将指针置空,避免出现重复释放的问题。

例题1:
请问以下代码会有什么样的结果?

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void GetMemory(char* p)
{
	p = (char*)malloc(100);
}

void Test()
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
}

int main()
{
	Test();
	return 0;
}

1、运行代码程序会出现崩溃的现象
2、程序存在内存泄漏的问题
str以值传递的形式给p
p是GetMemory函数的形参,只能函数内部有效
等GetMemory函数返回之后,动态开辟内存尚未释放
并且无法找到,所以会造成内存泄漏

改进1:

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

void GetMemory(char** p)
{
	*p = (char*)malloc(100);
}

void Test()
{
	char* str = NULL;
	GetMemory(&str);
	assert(str);
	strcpy(str, "hello world");
	printf(str);
	free(str);
	str = NULL;
}

int main()
{
	Test();
	return 0;
}

改进2:

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

char* GetMemory(char* p)
{
	p = (char*)malloc(100);
	return p;
}

void Test()
{
	char* str = NULL;
	str=GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
	free(str);
	str = NULL;
}

int main()
{
	Test();
	return 0;
}

例题2:
请问以下代码会有什么样的结果?

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

char* GetMemory()
{
	char p[] = "hello world";
	return p;
}

void Test()
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}

int main()
{
	Test();
	return 0;
}

非法访问。
p的内容已被回收,但地址返回了。

例题3:

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void GetMemory(char **p,int num)
{
	*p = (char*)malloc(num);
}

void Test()
{
	char* str = NULL;
	GetMemory(&str,100);
	strcpy(str, "hello");
	printf(str);
}

int main()
{
	Test();
	return 0;
}

内存泄漏。

例题4:

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void Test()
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}

int main()
{
	Test();
	return 0;
}

非法访问。
篡改动态内存区的内容,后果难以预料,非常危险。
因为free(str);之后,str成为野指针。if (str != NULL)不起作用。

在这里插入图片描述
图片转自比特科技

C/C++程序内存分配的几个区域:
1、栈区(stack):在执行函数时,函数内局部变量的储存单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令中,效率很高,但是分配的内存容量有限。栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
2、堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。分配方式类似于链表。
3、数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
4、代码段:存放函数体(类成员函数和全局函数)的二进制代码。

实际上普通的局部变量是在栈区分配空间的,栈区的特点是在上面创建的变量除了作用域就销毁。
但是被static修饰的变量存放在数据段(静态区),数据段的特点是在上面创建的变量,直到程序结束才销毁,所以生命周期变长。

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

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

相关文章

微服务项目租房网

文章目录 一、租房网项目的介绍1、使用的技术介绍2、使用的组件和开发工具的版本以及作用3、项目模块结构4、项目总体架构 二、环境搭建1、启动前端服务2、CentOS7各个组件的安装2.1 安装Docker2.2 安装JDK2.3 安装Redis(6390)2.4 安装FastDFS(8888)2.5 安装MongoDB(27017)2.6 …

Niagara—— 概述

目录 一&#xff0c;核心组件 Systems Emitters Modules Parameters 二&#xff0c;创建系统或发射器向导 System向导 Emetter向导 三&#xff0c;Niagara VFX工作流程 创建系统 创建或添加发射器 创建或添加模块 Niagara是最新一代VFX系统&#xff0c;无需程序员…

Junit测试框架详解

目录 Junit框架 导入Junit到项目 Junit注解 Test Disabled BeforeAll / AfterAll BeforeEach / AfterEach 参数化 单参数 多参数 CSV获取参数 方法获取参数 断言 assertEquals / assertNotEquals assertNull / assertNotNull 用例执行顺序 测试套件Suite 指定…

使用IIS创建WEB服务

文章目录 前言一、Web服务是什么&#xff1f;1.Web服务概述2.如何获取网页资源3.常见Web服务端软件4.什么是IIS 二、安装IIS1.安装Web服务器角色2.准备网页文件3.配置Web站点4.客户端浏览例&#xff1a;配置IIS站点 三、虚拟主机概述1.虚拟Web主机2.虚拟主机的几种类型3.基于端…

软考信管高级——进度管理

进度管理内容 缩短活动工期方法 赶工&#xff0c;投入更多资源或增加工作时间&#xff0c;以缩短关键活动的工期快速跟进&#xff0c;并行施工&#xff0c;以缩短关键路径长度使用高素质的资源或经验更丰富的人员减小活动范围或降低活动要求改进方法或技术&#xff0c;以提高…

活动回顾|解锁 AIGC 密码,探寻企业发展新商机

5月24日&#xff0c;Google Cloud 与 Cloud Ace 联合主办的线下活动顺利落下帷幕。 本次活动&#xff0c;有近 40 位企业精英到场支持。三位 Google Cloud 演讲嘉宾就本次活动主题&#xff0c;为大家带来了比较深度的演讲内容&#xff0c;干货满满。 &#xff08;*以下的嘉宾演…

期末复习总结【MySQL】聚合查询 + 多表联合查询(重点)

文章目录 前言一、聚合查询1, 聚合函数2, 聚合函数使用示例3, GROUP BY 子句4, HAVING 子句 二、联合查询(重点)1, 笛卡尔积2, 内连接2.1, 示例12.2, 示例22.3, 示例3 3, 外连接4, 自连接 总结 前言 各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你: &#…

存量时代下,互联网玩家如何“自我造血”?

毫无疑问&#xff0c;互联网已经进入存量时代。 在过去高增长的增量时代&#xff0c;许多互联网企业追求规模效应&#xff0c;痴迷于“先规模后盈利”的打法&#xff0c;力图用规模构建护城河。然而&#xff0c;随着行业整体增长速度放缓&#xff0c;规模扩张变得更为艰难&…

面了个字节跳动拿 38K 出来的测试,让我见识到了跳槽的天花板

最近内卷严重&#xff0c;各种跳槽裁员&#xff0c;相信很多小伙伴也在准备金九银十的面试计划。 作为一个入职5年的老人家&#xff0c;目前工资比较乐观&#xff0c;但是我还是会选择跳槽&#xff0c;因为感觉在一个舒适圈待久了&#xff0c;人过得太过安逸&#xff0c;晋升涨…

【JavaSE】Java基础语法(十六):抽象类

文章目录 1. 抽象类的概述2. 抽象类的特点3. 抽象类的实用价值4. 抽象类的案例 1. 抽象类的概述 当我们在做子类共性功能抽取时&#xff0c;有些方法在父类中并没有具体的体现&#xff0c;这个时候就需要抽象类了&#xff01; 在Java中&#xff0c;一个没有方法体的方法应该定义…

基于TCP、UDP网络编程

文章目录 网络协议分层套接字UDP和TCP差异UDP的APIDatagramSocketDatagramPacket 基于UDP Socket 实现一个回显程序TCP的APISocket的API基于TCP实现回显程序 网络协议分层 应用层&#xff1a; 应用程序拿到数据怎么用传输层&#xff1a; 负责关注传输过程中起点和终点网络层 &…

windows环境下nginx+ftp服务器搭建简易文件服务器

这里写目录标题 1&#xff0c;前言2&#xff0c;FTP服务器搭建3&#xff0c;nginx安装 1&#xff0c;前言 几种文件服务器的对比 1&#xff0c;直接使用ftp服务器&#xff0c;访问图片路径为 ftp://账户:密码192.168.0.106/31275-105.jpg不采用这种方式&#xff0c;不安全容易…

【高效科研工具(二):使用NewbingChat(方法+问题)| Newbing帮你阅读paper、解析paper】

高效科研工具&#xff08;二&#xff09;&#xff1a;使用NewbingChat&#xff08;方法问题&#xff09;| Newbing帮你阅读paper、解析paper 目录 0、前言 1、NewbingChat 介绍 2、NewbingChat 注册方法 3、NewbingChat 遇到的问题 (&#x1f1fa;&#x1f1f8;位置切换问题、…

STM32H7B0VBT6使用Free RTOS配置SD卡+Fatfs文件管理系统

作者&#xff1a;Jack_G 时间&#xff1a;2023.05.26 版本&#xff1a;V1.0 上次修改时间&#xff1a; 环境&#xff1a; \quad \quad \quad \quad STM32Cube MX V6.8.1 \quad \quad \quad \quad STM32CubeH7 Firmware Package V1.11.0 / 04-Nov-2022 \quad \quad \quad \qu…

基于混合蛙跳的路径规划算法

路径规划算法&#xff1a;基于混合蛙跳优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于混合蛙跳优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化…

改变开发的未来 | 探索无服务器与人工智能的协同效应

近年来&#xff0c;无服务器计算和人工智能深刻改变着应用程序的开发方式。 无服务器计算实现无需管理底层基础架构就能构建和运行应用程序&#xff0c;而人工智能则让应用程序依据数据和算例做出智能决策。借助云计算&#xff0c;开发者打开了一个应用程序开发、构建的全新世…

Linux网络编程——有限状态机

在逻辑单元内部的一种高效的编程方法&#xff1a;有限状态机。 有的应用层协议头部包含数据包类型字段&#xff0c;每种类型可以映射为逻辑单元的一种执行状态&#xff0c;服务器可以根据它来编写相应的处理逻辑&#xff0c;下面代码展示的是状态独立的有限状态机 STATE_MACH…

Python中模块的使用3

在运行Python程序时&#xff0c;总会用到Python的标准库模块。一些标准库模块被内嵌到Python解释器中&#xff0c;通过调用这些模块提供的函数&#xff0c;可以实现特殊的功能。sys模块就是Python的一个标准库模块&#xff0c;该模块被被内嵌到Python解释器中。 1 sys模块的导…

阿里云备案服务码申请方法流程

阿里云备案服务码是什么&#xff1f;ICP备案服务码怎么获取&#xff1f;阿里云备案服务码分为免费和付费两种&#xff0c;申请备案服务码是有限制条件的&#xff0c;需要你的阿里云账号下有可用于申请备案服务码的云产品&#xff0c;如云服务器、建站产品、虚拟主机等&#xff…

计算机组成原理-指令系统-指令格式及寻址方式

目录 一、指令的定义 1.1 扩展操作码指令格式 二、指令寻址方式 2.1 顺序寻址 2.2 跳跃寻址 三、 数据寻址 3.1 直接寻址 3.2 间接寻址 3.3 寄存器寻址 ​ 3.4 寄存器间接寻址 3.5 隐含寻址 3.6 立即寻址 3.7 偏移地址 3.7.1 基址寻址 3.7.2 变址寻址 3.7.3 相对寻址…