【动态内存】详解

Hi~!这里是奋斗的小羊,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~
💥💥个人主页:奋斗的小羊
💥💥所属专栏:C语言

🚀本系列文章为个人学习笔记,在这里撰写成文一为巩固知识,二为展示我的学习过程及理解。文笔、排版拙劣,望见谅。


目录

  • 1、常见动态内存错误
      • 1.1 对NULL指针的解引用操作
      • 1.2 对动态内存空间的越界访问
      • 1.3 对非动态开辟内存使用free释放
      • 1.4 使用free释放动态内存的一部分
      • 1.5 对同一快动态内存多次释放
      • 1.6 动态开辟内存忘记释放(内存泄漏)
  • 2、动态内存经典笔试题分析
      • 2.1 题目一
      • 2.2 题目二
      • 2.3 题目三

1、常见动态内存错误

1.1 对NULL指针的解引用操作

如果我们写的代码不严谨,没有考虑到动态内存分配失败的可能,就会写出类似于下面的代码:

#include <stdio.h>
#include <stdlib.h>int main()
{int* p = (int*)malloc(10 * sizeof(int));//直接使用指针pint i = 0;for (i = 0; i < 10; i++){p[i] = i + 1;}return 0;
}

这样的代码可能并没有什么问题,但是存在很大的隐患,因为动态内存函数是有可能开辟内存空间失败的,当开辟失败时会返回NULL,而NULL指针是不能解引用的
像VS这样比较强大的编译器会立马检测到并提示你
在这里插入图片描述

为了避免这种错误,我们需要对指针p进行判断,再决定是否使用

#include <stdio.h>
#include <stdlib.h>int main()
{int* p = (int*)malloc(10 * sizeof(int));//判断p是否为空指针if (p == NULL){//打印出错误信息perror("malloc");//终止程序return 1;}int i = 0;for (i = 0; i < 10; i++){p[i] = i + 1;}return 0;
}

1.2 对动态内存空间的越界访问

我们用动态内存函数开辟多大的空间,我们就使用多大的空间,不能越界访问,例如:

#include <stdio.h>
#include <stdlib.h>int main()
{int* p = (int*)malloc(10 * sizeof(int));//判断p是否为空指针if (p == NULL){//打印出错误信息perror("malloc");//终止程序return 1;}int i = 0;//p+1跳过1个整型,p+10就会越界for (i = 0; i <= 10; i++){p[i] = i + 1;}return 0;
}

聪明的VS也会检测出错误提示你
在这里插入图片描述


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

free函数是用来释放由动态内存函数开辟的空间的,不能释放普通内存

#include <stdio.h>
#include <stdlib.h>int main()
{int arr[10] = { 0 };int* p = arr;free(p);p = NULL;return 0;
}

当我们运行起来后就出问题了
在这里插入图片描述


1.4 使用free释放动态内存的一部分

上面我们用malloc函数申请了10个整型空间,然后通过for循环给这10个整型空间内放1~10的整数,有些同学可能会为了方便这样写代码:

#include <stdio.h>
#include <stdlib.h>int main()
{int* p = (int*)malloc(10 * sizeof(int));//判断p是否为空指针if (p == NULL){//打印出错误信息perror("malloc");//终止程序return 1;}//给申请的动态空间内存1~10int i = 0;for (i = 0; i < 5; i++){*p++ = i;}//释放动态内存空间free(p);p = NULLreturn 0;
}

当我们运行起来才发现写出了BUG
在这里插入图片描述
这又是为什么呢?
事实上此时free(p)中的p指针已经不再指向malloc开辟的动态内存的起始地址了,因为*p++这里对p的指向不断递增

free操作的指针必须指向要被释放的动态内存的起始地址


1.5 对同一快动态内存多次释放

当我们用完一块动态内存空间后不再使用对其释放后,可能会因为忘记而重复释放一次,并且如果第一次释放时忘记给p指针赋NULL,那么程序就会出错

	//使用...//释放动态空间free(p);//...free(p);p = NULL;return 0;

但是如果我们两次释放时都给p指针赋了NULL,那基本不会发生什么事,相当于没有错,只是逻辑上讲不通
所以,在我们用free释放完动态内存空间后,紧跟着对指针赋NULL是很有必要的


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

动态开辟的空间一定要释放,并且正确释放

当我们写代码的时候,存在这样一种可能会出现的错误,那就是动态开辟的内存忘记释放或者因为某些原因还没有到free语句就提前终止代码,这里举个简单的例子

#include <stdio.h>
#include <stdlib.h>void text()
{int flag = 1;int* p = (int*)malloc(100);if (p == NULL){return 1;}//使用//因为某些原因函数提前返回了if (flag == 1){return;}//free函数free(p);p = NULL;
}int main()
{//自定义函数text();//后面还有大量代码//....return 0;
}

虽然我们确实用了free函数释放空间,但是当代码量较大时可能会因为某些原因还没到free函数就提前终止了,而我们还没意识到,就算后面我们意识到了这个问题这块内存我们也找不到了
只有整个程序结束后这块内存才能被释放,如果程序一直不结束这块空间就再也找不到了,这就叫内存泄漏

所以,就算动态内存申请使用后用了free,也是有可能犯内存泄漏的错误,我们要多加小心

内存泄漏是比较可怕的,尤其是某些24小时不断运行的服务器程序,如果存在内存泄漏,内存被耗干也只是时间的问题


2、动态内存经典笔试题分析

2.1 题目一

请问运行下面 text函数会有什么后果?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>void get_memory(char* p)
{p = (char*)malloc(100);
}void text(void)
{char* str = NULL;get_memory(str);strcpy(str, "hello world");printf(str);
}int main()
{text();return 0;
}

上面的代码一共有两个问题
第一个问题:malloc申请动态内存空间后没有使用free函数释放,这可能会导致内存泄漏

#include <stdio.h>
#include <stdlib.h>
#include <string.h>void get_memory(char* p)
{p = (char*)malloc(100);
}void text(void)
{char* str = NULL;get_memory(str);strcpy(str, "hello world");printf(str);free(str);str = NULL;
}int main()
{text();return 0;
}

第二个问题: 函数传参传值调用和传址调用使用错误

这个代码的意思是申请一块动态内存空间地址交给指针p,通过指针p再交给指针str,再使用strcpy函数将字符串拷贝到动态内存空间内,最后打印出字符串
但是get_memory函数传参的时候使用的是传值调用,所以指针p跟指针str没有关系

有两种纠错方法
方法一: 将传值调用改为传址调用,此时p为二级指针

#include <stdio.h>
#include <stdlib.h>
#include <string.h>void get_memory(char** p)
{*p = (char*)malloc(100);
}void text(void)
{char* str = NULL;get_memory(&str);strcpy(str, "hello world");printf(str);free(str);str = NULL;
}int main()
{text();return 0;
}

在这里插入图片描述

方法二: 直接返回指针p的地址,不需要传参

char* get_memory()
{char* p = (char*)malloc(100);return p;
}void text(void)
{char* str = NULL;str = get_memory();strcpy(str, "hello world");printf(str);free(str);str = NULL;
}int main()
{text();return 0;
}

在这里插入图片描述


2.2 题目二

请问运行下面 text函数会有什么后果?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>char* get_memory(void)
{char p[] = "hello world";return p;
}void text(void)
{char* str = NULL;str = get_memory();printf(str);
}int main()
{text();return 0;
}

上面的代码是一个非常经典的例子,之前在C语言(指针)3中野指针一小节介绍过类似的例子

上面代码的问题:
我们在自定义函数get_memory中创建了一个局部临时数组存入字符串“hello world”,再将字符串的首地址返回用指针str接收,虽然此时指针str确实指向字符串“hello world”的首地址,但是此时str是没有权限访问这块空间的

因为在局部数组p在出了get_memory函数后就销毁了,它申请的空间会被收回,即使指针str能找到这块空间,但是它已经没有权限使用了,此时str就是一个野指针

在这里插入图片描述

所以我们应该避免返回栈空间地址

想要改正上面的代码也很简单,我们申请一块动态内存就行,同时也别忘了释放

#include <stdio.h>
#include <stdlib.h>
#include <string.h>char* get_memory(void)
{char* p = (char*)malloc(20);strcpy(p, "hello world");return p;
}void text(void)
{char* str = NULL;str = get_memory();printf(str);free(str);str = NULL;
}int main()
{text();return 0;
}

2.3 题目三

请问运行下面 text函数会有什么后果?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>void get_memory(char** p, size_t num)
{*p = (char*)malloc(num);
}void test(void)
{char* str = NULL;get_memory(&str, 100);strcpy(str, "hello world");printf(str);
}int main()
{test();return 0;
}

上面的代码是可以打印出“hello world”的,但是遗憾的是上面的代码中使用了动态内存函数malloc,但是没有使用free函数释放动态内存空间
虽然上面的代码可以实现我们想要的效果,但这样的代码是存在安全隐患的

动态内存开辟函数malloccallocrealloc和动态内存释放函数free必须成对出现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>void get_memory(char** p, size_t num)
{*p = (char*)malloc(num);
}void test(void)
{char* str = NULL;get_memory(&str, 100);strcpy(str, "hello");printf(str);free(str);str = NULL;
}int main()
{test();return 0;
}

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

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

相关文章

【Git】版本控制器的方式:SVN集中式版本控制工具和Git分布式版本控制工具

一、应用场景 二、版本控制器的方式 三、SVN 集中式版本控制工具 四、Git 分布式版本控制工具 五、Git工作流程 一、应用场景 Git 在开发过程中提供了多种应用场景&#xff0c;帮助开发团队高效地管理代码、协同工作&#xff0c;并保证代码质量。以下是一些具体应用场景和相应…

Rocky Linux设置静态IP

[connection] idens160 uuidcd246f67-c929-362a-809d-f1b44ddc5d25 typeethernet autoconnect-priority-999 interface-nameens160 timestamp1719094243[ethernet][ipv4] ## 在IPV4下面修改如下内容 methodmanual address192.…

每日一题——Python实现PAT乙级1030 完美数列(举一反三+思想解读+逐步优化)五千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 初次尝试 再次尝试 代码结构 时间复杂度分析 空间复杂度分析 总结 我要更强 时…

【OpenCV 图像处理 Python版】图像处理的基本操作

文章目录 1.图像的 IO 操作1.1 图像读取 imread1.2 图像显示1.2.1 opencv 方式1.2.2 matplotlib 方式 1.3 图像保存 imwrite 2.绘制几何图形1. 绘制直线2. 绘制矩形3. 绘制圆形4. 绘制多边形5. 添加文字 3.获取并修改图像中的像素点3.1 获取像素值3.2 修改像素值3.3 获取和修改…

零基础想学编程,选择哪一门语言更好就业?(非常详细)零基础入门到精通,收藏这一篇就够了_c#就业方向

编程语言的用途广泛&#xff0c;它们各自在不同的领域和应用场景中发挥着重要作用。 零基础初学者在选择编程语言时&#xff0c;可以从就业方向入手选择。 【一一帮助安全学习&#xff0c;所有资源获取处一一】 ①网络安全学习路线 ②20份渗透测试电子书 ③安全攻防357页笔记 …

Maven高级理解属性

属性 在这一章节内容中&#xff0c;我们将学习两个内容&#xff0c;分别是 属性版本管理 属性中会继续解决分模块开发项目存在的问题&#xff0c;版本管理主要是认识下当前主流的版本定义方式。 4.1 属性 4.1.1 问题分析 讲解内容之前&#xff0c;我们还是先来分析问题: …

pdf压缩,pdf压缩在线,pdf压缩在线网页版

当我们遇到PDF文件过大&#xff0c;需要压缩其容量大小时&#xff0c;通常是为了更方便地传输、存储或分享这些文件。PDF文件的大小可能因其包含的图像、字体等元素的数量和质量而有所不同。下面&#xff0c;我们将详细介绍压缩PDF容量大小的方法&#xff0c;帮助您轻松实现文件…

Vite打包速度为什么比webpack快,打包的优劣势在哪里?

大家都有被webpack打包速度搞崩溃的时候&#xff0c;修改一处地方&#xff0c;想预览效果&#xff0c;要等上半天。 Vite比Webpack快的原因 ESM&#xff08;ES Module&#xff09;原生支持&#xff1a; Vite基于ESM构建&#xff0c;利用浏览器原生支持的ESM模块加载方式&…

基于 JuiceFS 构建高校 AI 存储方案:高并发、系统稳定、运维简单

中山大学的 iSEE 实验室&#xff08;Intelligence Science and System) Lab&#xff09;在进行深度学习任务时&#xff0c;需要处理大量小文件读取。在高并发读写场景下&#xff0c;原先使用的 NFS 性能较低&#xff0c;常在高峰期导致数据节点卡死。此外&#xff0c;NFS 系统的…

【PL理论深化】(7) Ocaml 语言:静态类型语言 | 自动类型推断 | 多态类型和多态函数 | let-多态类型系统

&#x1f4ac; 写在前面&#xff1a;OCaml 是一种拥有静态类型系统的语言&#xff0c;本章我们就要探讨静态类型系统。 目录 0x00 静态类型系统 0x01 自动类型推断&#xff08;automatic type inference&#xff09; 0x02 多态类型和多态函数 0x03 let-多态类型系统&#…

暴雨虐长沙,生灵受煎熬

今天&#xff0c;“湖南长沙市遭遇强降雨,一小时的降雨量足够注满54个西湖”这消息&#xff0c;终于登上互联网社交平台热搜榜。 截图&#xff1a;来源社交网站 综合多家媒体消息概述如下。 昨&#xff08;24日&#xff09;天&#xff0c;湖南长沙市遭遇强降雨&#xff0c;一…

AI赋能影视解说:Rap说唱玩法拆解!

在影视解说的领域&#xff0c;竞争一直非常激烈&#xff0c;众多创作者纷纷涌入这个热门的赛道。为了在众多声音中脱颖而出&#xff0c;创新成为了关键。最近&#xff0c;一种结合AI技术的解说方式——Rap说唱解说&#xff0c;以其新颖的形式和高效的创作过程&#xff0c;赢得了…

kingbase R3修改pcp_watchdog密码

需要修改的文件 kingbasecluster/etc/HAmodule.conf kingbasecluster/etc/pcp.conf db/etc/HAmodule.conf 3个文件 两个节点都要改。 1.pcp.conf文件 cd kingbasecluster/bin ./sys_md5 12345678ab! 将结果复制拷贝到…/etc/pcp.conf中 放到kingbase:后面 也就是把kingbase 的密…

Java面试八股之JVM内存溢出的原因及解决方案

JVM内存溢出的原因及解决方案 JVM内存溢出&#xff08;Out Of Memory&#xff0c;OOM&#xff09;通常是由于程序运行过程中内存使用不当造成的&#xff0c;常见原因及相应的解决方案如下&#xff1a; 原因及解决方案 内存中加载的数据量过大 原因&#xff1a;一次性从数据…

展厅设计中需要人性化的地方

1、预留参观空间 展厅空间的布局设计必须尽可能的宽敞&#xff0c;以避免参观人数较多时可能会发生的拥堵&#xff0c;重点展品需要预留较大的展示空间或四面通畅的中心位置&#xff0c;更方便观众从不同角度与方位参观。因为是展厅&#xff0c;不仅代表着企业形象&#xff0c;…

SolidWorks北京正版代理商亿达四方:官方授权SolidWorks中国代理

在北京这座融合了古老文明与现代科技的都市中&#xff0c;亿达四方作为SolidWorks官方认证的北京区域正版代理商&#xff0c;正引领着一场设计与制造领域的革新风潮。我们致力于为北京及周边地区的企业提供原汁原味的SolidWorks软件及全方位的增值服务&#xff0c;共同推进首都…

智慧校园-毕业管理系统总体概述

在当今教育信息化的浪潮中&#xff0c;智慧校园毕业管理系统脱颖而出&#xff0c;它作为一项综合性的数字平台&#xff0c;全面覆盖了从毕业资格审查到学位授予的每一个关键步骤&#xff0c;旨在通过智能化手段&#xff0c;为高校的毕业管理工作带来革命性的变革。毕业管理系统…

针对VMWare无法使用鼠标功能键问题

在使用 VMWare 虚拟机的Ubuntu系统时发现无法使用许多鼠标带有额外的功能键&#xff0c;比如常用的前进后退&#xff0c;但是双系统中的Ubuntu没有问题&#xff0c;后来一搜发现是&#xff0c;虚拟系统中不支持这些功能键。因此我们对这个问题进行了解决。 解决方案 1.找到自…

【技术解码】百数SRM:如何助力企业快速优化供应链管理?

SRM应用是企业优化供应链管理的重要工具&#xff0c;它帮助企业全面管理供应商关系&#xff0c;从评估、选择到协同合作和绩效监控&#xff0c;确保供应链的稳定性和效率。 对于企业来说&#xff0c;通过全面管理供应商关系&#xff0c;可以降低采购风险&#xff0c;提升产品质…

深度学习windows环境配置

1 下载CUDA和cudnn 详见文章 CUDA与CUDNN在Windows下的安装与配置&#xff08;超级详细版&#xff09;_windows cudnn安装-CSDN博客 我电脑的CUDA下载链接如下 ​​​​​https://developer.nvidia.com/cuda-12-1-0-download-archive?target_osWindows&target_archx86…