动态内存管理(malloc,calloc,realloc,free)+经典笔试题

动态内存管理

  • 一. malloc 和 free
    • 1. malloc
    • 2. free
  • 二. calloc
  • 三. realloc
  • 四.动态内存的错误
    • 1.对NULL指针的解引用操作
    • 2.对动态开辟空间的越界访问
    • 3.对非动态开辟内存使用free释放
    • 4.使用free释放一块动态开辟内存的一部分
    • 5.对同一块动态内存多次释放
    • 6.动态开辟内存忘记释放(内存泄漏)
  • 五.动态内存经典笔试题分析

前言:

  1. 当我们要开辟一块连续的内存空间时,我们第一时间想到的可能是数组。但是一但开辟了数组,数组的大小就确定了,无法调整数组的大小。
  2. 有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了。
  3. 于是动态内存开辟函数(malloc,calloc,realloc,free)应运而生,下文带您一一了解其中的奥秘。

一. malloc 和 free

1. malloc

void* malloc(size_t size);

解释:在堆区中开辟一块大小为 size 个字节的空间,返回指向这块空间的起始地址(泛型指针void*)

因为这块空间存放的数据类型不知(由程序员自己确定),所以用泛型指针接收该地址,在使用的时候记得养成一个好习惯:强制类型转换为自己需要的数据类型。

  1. 如果开辟成功,则返回一个指向开辟好空间的指针。

  2. 如果开辟失败,则返回一个 NULL 指针,因此malloc的返回值一定要做检查。

  3. 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候程序员自己来决定。

  4. 如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

2. free

void free(void* ptr);

解释:free是用来对动态内存的释放和回收的。free 对指针 ptr 指向的内容释放掉,但是指针仍然指向这块空间,若后面不再使用,及时将 ptr 置为 NULL,否则产生野指针。

  1. 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。

  2. 如果参数 ptr 是NULL指针,则函数什么事都不做。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
	//在堆区申请10个整形空间
	int* p=(int*)malloc(10*sizeof(int));
	if (p == NULL)
	{
		//开辟空间失败
		perror("malloc");//打印错误信息
		//printf("%s\n", strerror(errno));//也是打印错误信息
		return 1;
	}
	//使用这块空间
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i + 1;
	}
	//打印这块空间
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	//释放这块空间
	free(p);//将这块空间还给了操作系统,我们已经没有权限再使用这块空间了
	        //但是p仍然指向那块空间

	p = NULL;//若不将p置为NULL,那么p就是野指针
	return 0;
}

在这里插入图片描述

总结:

  1. 动态内存开辟的函数头文件都是 stdlib.h。
  2. 如果不释放的话,程序结束的时候也会被操作系统自动释放。
  3. 但是为了防止内存泄漏,将其置为NULL。这是一个好习惯。

二. calloc

void* calloc(size_t num, size_t size);

解释:在堆区中开辟一块大小为 num * size 个字节的空间,返回指向这块空间的起始地址,其中 num 为数据的个数,size 为单个数据的字节数,同时把申请的空间的每个字节初始化为全为0。


#include<stdio.h>
#include<stdlib.h>
int main()
{
	//在堆区申请10个整形空间
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
		perror("calloc");
		return 1;
	}
	//使用空间
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	//释放
	free(p);
	p = NULL;
	return 0;
}

在这里插入图片描述

三. realloc

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

解释:调整动态内存开辟的空间,ptr 是那块空间的起始地址,size 是调整后的那块空间的字节的个数,返回指向这块空间的起始地址。

#include<stdio.h>
#include<stdlib.h>
int main()
{
	//在堆区申请10个整形空间
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//调整空间——变成20个整形空间
	int* ptr = (int*)realloc(p, 20 * sizeof(int));//注意:要用新的指针来接收
	if (ptr != NULL)
	{
		p = ptr;
	}
	else
	{
		//开辟失败
		return 1;
	}
	int i = 0;
	for (i = 0; i < 20; i++)
	{
		*(p + i) = i + 1;
	}
	for (i = 0; i < 20; i++)
	{
		printf("%d ", *(p + i));
	}
	//释放
	free(p);
	p = NULL;
	return 0;
}

在这里插入图片描述

注意:也许有些人有疑问为什么要用新的指针接收返回的地址,直接用原来的指针接收不行吗?答案是不行的,在realloc调整动态内存开辟的空间有3中情况,代码如下:

int main()
{
	int* p = (int*)malloc(10);
	//...
	if (p != NULL)
	{
		int* ptr = (int*)realloc(p, 20);
		//...
	}
	return 0;
}

情况1:

  1. 开辟的空间后面有足够且连续的空间,只需返回空间的起始地址即可。
    在这里插入图片描述

情况2:

  1. 如果后续的空间不够,realloc 函数直接在堆区找一块新的满足大小的空间,将旧的地址,拷贝到新的地址。
  2. 自动释放旧的地址指向的空间,不需要手动 free,返回新的空间的起始地址。

在这里插入图片描述

情况3:

  1. 堆区已经没有满足情况的连续空间了,返回NULL。
    在这里插入图片描述

realloc函数也能开辟空间,代码如下:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)realloc(NULL, 10 * sizeof(int));//等价于malloc(40)
	if (p == NULL)
	{
		//...
	}
	return 0;
}

四.动态内存的错误

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

  1. 如果将一个空指针(NULL)进行解引用操作,程序会遇到未定义行为,会导致程序崩溃。这是因为空指针并不指向任何有效的内存地址,尝试解引用它会导致访问非法内存,从而导致程序崩溃。
  2. 因此,在解引用指针之前,应该始终先检查指针是否为空。

错误代码如下:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(10 * sizeof(int));//可能会开辟失败导致,p等于NULL
	//if (p == NULL)
	//{
	//	  perror("malloc");
	//	  return 1;
	//}
	
	//使用
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i + 1;//如果p等于NULL,对其进行解引用操作,程序会崩溃
	}

	free(p);
	p = NULL;
	return 0;
}

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

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//使用
	int i = 0;
	for (i = 0; i < 40; i++)//越界访问,程序崩溃
	{
		*(p + i) = i + 1;
	}

	free(p);
	p = NULL;
	return 0;
}

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

#include<stdio.h>
int main()
{
	int a = 10;
	int* p = &a;
	//...

	free(p);//程序崩溃
	p = NULL;
	return 0;
}

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

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//使用
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*p = i;
		p++;//修改了指针p
	}

	free(p);//free释放一块动态开辟内存的一部分,程序崩溃
	p = NULL;
	return 0;
}

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

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//使用
	free(p);//p为野指针

	//...
	free(p);//对同一块动态内存多次释放,程序崩溃
	p = NULL;
	return 0;
}

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

  • 内存泄漏:在程序执行过程中,动态分配的内存空间在程序不再需要时没有被正确释放的情况。这会导致程序在运行过程中持续耗费内存空间而不释放,最终可能导致系统性能下降,甚至导致程序崩溃。
#include<stdio.h>
#include<stdlib.h>
void test()
{
	int flag = 1;
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
		return;
	//使用
	if (flag)
		return;//未释放,函数提前结束,就找不到那块空间,导致内存泄漏
		
	free(p);
	p = NULL;
}
int main()
{
	test();
	//......
	//只有程序结束了,空间才被释放
	return 0;
}
  1. 在一些服务器上(腾讯,阿里…),可能7*7=49天一直在运行,若一直申请内存而不释放,内存迟早有一天会耗尽的,这会造成巨大损失。
  2. 动态内存管理是一把双刃剑:提供灵活的内存管理方式,但是会带来风险。
  3. 切记:动态开辟的空间一定要释放,并且正确释放。

五.动态内存经典笔试题分析

例题1:

在这里插入图片描述
解决办法:

  1. 传递 str 的地址通过地址修改 str ,同时可以释放动态内存开辟的空间。
  2. 返回动态内存开辟的空间的地址,可以释放动态内存开辟的空间。
    在这里插入图片描述

例题2:

在这里插入图片描述

例题3:

在这里插入图片描述

解决办法:
在这里插入图片描述

创作不易,如果能帮到你的话能赏个三连吗?感谢啦!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

streamlit:如何快速构建一个应用,不会前端也能写出好看的界面

通过本文你可以了解到&#xff1a; 如何安装streamlit&#xff0c;运行起来第一个demo熟悉streamlit的基本语法&#xff0c;常用的一些组件使用streamlit库构建应用 大模型学习参考&#xff1a; 大模型学习资料整理&#xff1a;如何从0到1学习大模型&#xff0c;搭建个人或企业…

SQL Chat:从SQL到SPEAKL的数据库操作新纪元

引言 SQL Chat是一款创新的、对话式的SQL客户端工具。 它采用自然语言处理技术&#xff0c;让你能够像与人交流一样&#xff0c;通过日常对话的形式对数据库执行查询、修改、创建及删除操作 极大地简化了数据库管理流程&#xff0c;提升了数据交互的直观性和效率。 在这个框…

【漏洞复现】用友NC pagesServlet SQL注入漏洞(XVE-2024-13067)

0x01 产品简介 用友NC是由用友公司开发的一套面向大型企业和集团型企业的管理软件产品系列。这一系列产品基于全球最新的互联网技术、云计算技术和移动应用技术&#xff0c;旨在帮助企业创新管理模式、引领商业变革。 0x02 漏洞概述 用友NC /portal/pt/servlet/pagesServlet…

关于 Redis 中集群

哨兵机制中总结到&#xff0c;它并不能解决存储容量不够的问题&#xff0c;但是集群能。 广义的集群&#xff1a;只要有多个机器&#xff0c;构成了分布式系统&#xff0c;都可以称之为一个“集群”&#xff0c;例如主从结构中的哨兵模式。 狭义的集群&#xff1a;redis 提供的…

MySQLWorkbench导出sql文件

MySQLWorkbench导出sql文件 前言效果图导出操作选择要导出的数据库遇到的问题解决问题查看mysql路径前言 在完成数据库搭建之后,需要为上线做准备,那么就需要导出数据库的建库sql了 本篇文章讲解的是mysql Workbench 导出数据建库脚本 效果图 导出操作 选择要导出的数据库…

java 深拷贝和浅拷贝

深拷贝和浅拷贝的概念 浅拷贝&#xff1a;只对基本数据类型进行拷贝&#xff0c;针对于引用数据类型&#xff0c;只是拷贝了对象的引用&#xff0c;没有真实的创建一个新的对象就是浅拷贝 深拷贝&#xff1a;在对引用数据类型拷贝过程中&#xff0c;创建了一个新的对象&#xf…

Dell服务器根据GPU温度调整风扇转速

前言 dell服务器自动风扇是根据CPU温度来调速的&#xff0c;我跑AI的时候cpu温度不高但是GPU温度很高导致显卡卡死PVE虚拟机直接挂起无法运行&#xff0c;我看了下也没有基于显卡温度调速的脚本&#xff0c;于是我就自己写了一个 基于ipmi工具 乌班图等linux先安装ipmi apt …

算法金 | A - Z,115 个数据科学 机器学习 江湖黑话(全面)

大侠幸会&#xff0c;在下全网同名「算法金」 0 基础转 AI 上岸&#xff0c;多个算法赛 Top 「日更万日&#xff0c;让更多人享受智能乐趣」 机器学习本质上和数据科学一样都是依赖概率统计&#xff0c;今天整整那些听起来让人头大的机器学习江湖黑话 A - C A/B Testing (A/B …

电子电器架构——智能座舱设备终端

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

电路笔记 : 嘉立创EDA 导入、查找、设计管理器(快速寻找网络标签)功能+DRC错误检查和处理

导入功能 查找功能 可查找多种类型&#xff0c;如原件名称、网络标签等 设计管理器 图层查看 DRC错误 规则设置 线距问题 大多数PCB制造商能够可靠地生产5 mil间距的走线和间隙。这是一个常见的标准&#xff0c;适合大多数消费级和工业级电子产品。在5 mil以上的间距&#xff…

设计软件有哪些?照明工具篇,渲染100邀请码1a12

阴影和照明涉及到图片的真实感和氛围&#xff0c;所以熟练使用照明工具是设计师的必备能力&#xff0c;这次我们介绍一些照明工具。 1、VRaySun VRaySun是VRay渲染器中的一个功能&#xff0c;用于模拟太阳光源。它是一种方便易用的光源类型&#xff0c;能够产生逼真的日光效果…

MySQL与PostgreSQL关键对比三(索引类型)

目录 索引类型 B-tree 索引 Hash 索引 Full-text 索引 GiST 索引 GIN 索引 BRIN 索引 索引创建示例 MySQL PostgreSQL 结论 以下SQL语句的执行如果需要开发工具支持&#xff0c;可以尝试使用SQLynx或Navicat来执行。 MySQL和PostgreSQL在索引方面有许多相似之处&am…

Puppeteer实战案例:自动化抓取社交媒体上的媒体资源

在当今数字化时代&#xff0c;社交媒体已成为人们获取信息、分享生活和进行商业推广的重要平台。随着社交媒体内容的爆炸性增长&#xff0c;自动化抓取社交媒体上的媒体资源变得尤为重要。本文将介绍如何使用Puppeteer这一强大的自动化工具来实现这一目标。 1. Puppeteer简介 …

从零开始理解AdaBoost算法:设计思路与算法流程(二)【权值更新与加权表决、数学公式】

设计思路 AdaBoost算法属于Boosting算法家族中的一种&#xff0c;其基本思路是将多个弱分类器组合成一个强分类器。 “强分类器”是指一个分类准确率较高的模型“弱分类器”则是指分类准确率略高于随机猜测的简单模型。 AdaBoost的核心思想是通过 加权 的方式逐步提高分类器…

在Modelarts上微调量化Llama3,并用docker部署

本文概述 本文先使用llama-factory去微调llama3大模型&#xff0c;然后使用llama.cpp去量化模型并打包为docker部署到服务器上让qq机器人能够调用服务&#xff0c;实现qq群内问答。 效果展示 环境准备 本文使用华为云的Modelarts的notebook去进行的模型微调 ubuntu20.04&#x…

黑马es学习

es 0. 基础概念0.1 倒排索引0.2 文档、索引0.3 与mysql对比 1 基本操作1.1 mapping 索引库操作1.2 单个文档CRUD 3. DSL查询3.1 查询所有3.2 全文检索3.3 精确查询3.4 复合查询-相关性得分3.5 分页3.6 高亮3.7 总结 2. RestClientmysql与es数据同步es集群去重 黑马视频 官方使…

【李宏毅-生成式 AI】Spring 2024, HW5:LLM Fine-tuning 实验记录

文章目录 1. Task Overview2. Overall Workflow3. Dataset 介绍4. 代码介绍4.1 环境介绍4.2 下载 dataset4.3 下载并加载模型4.2 Notebook 代码1&#xff09;import 部分2&#xff09;固定 seed3&#xff09;加载 LLM4&#xff09;加载 tokenizer5&#xff09;设置解码参数6&am…

【Linux系统化学习】传输层——TCP协议

目录 预备知识 全双工协议 协议缓冲区 TCP协议 TCP协议格式 六个标志位 两个问题 确认应答机制 流量控制 超时重传机制 连接管理机制 CLOSE_WAIT状态 TIME_WAIT状态 滑动窗口 拥塞控制 延迟应答 捎带应答 粘包问题 TCP的异常情况 TCP小结 TCP/UDP协议对比…

数据结构:插入排序和希尔排序

插入排序 逆序的情况下&#xff1a; 时间复杂度&#xff1a;O(N^2) 空间复杂度&#xff1a;O(1) 顺序的情况下&#xff1a; 时间复杂度&#xff1a;O(N) 空间复杂度…

【教程】怎么获取IPV6,我教你

1.png 所以IPV6诞生了 IPV6拥有超大的地址空间 IPv4 采用 32 位地址长度&#xff0c;可以为我们提供 2^32 大约 43 亿个地址&#xff0c;而 IPv6 采用 128 位地址长度&#xff0c;为我们提供了 2^128 个地址 博主的家里用的宽带是移动宽带&#xff0c;众所周知&#xff0c;…