跟我学C++中级篇——典型的内存问题分析

一、内存问题引起的Crash

程序的崩溃对每个开发人员来说,都是一种磨难的存在,不经历不会成长,但再怎么成长也不愿意经历。在程序崩溃的现象中,内存引起的程序崩溃一直是重要的原因,也可以说,内存的异常引起的程序崩溃是一种较大概率的存在。

二、典型的内存问题

内存问题有很多种,本篇文章主要分析两个典型的情况:
1、缓冲区溢出
Buffer Overflow,一般也可以认为是内存越界。这种现象对于初学者和中级开发者来说非常常见。它的危害性不光局限于引起程序崩溃,更可怕是的,黑客可以利用这些溢出的缓冲区植入各种木马和病毒,从而产生更大的危害。
最常见的几种缓冲区溢出的情况有:
1)数组(类似数组的内存区等)越界访问
2)指针的访问越界(比如拷贝大数据到一个小内存等 )
3)这里还要专门把字符串拿出来说事儿,在做字符串的拷贝、搜索定位等情况下,防止出现溢出。
而面对这种比较经典的内存问题,目前的避免或者说解决手段也比较多。常见的有:
1)代码检查,比如使用一个内存检测工具(Valgrind或Cppcheck等)或人工Reivew等,查看是否有内存越界
2)使用编译器进行自动检查,比如编译器经常会有一些内置的安全选项,如gcc中的-fsanitize=address
3)开发者的代码控制手段,也有几种方法,首先是增加对内存宽度的检查,如数组的长度,动态分配内存的长度是否匹配等,其次是使用一些更高级的函数,这些函数内部其实是增加了类似内存长度的检查这种机制,比如strcpy函数改为strncpy函数(可能在Windows和Linux下略有不同),再次就是可以借助一些库提供的高级的数据结构,降低对内存的直接依赖,诸如类似于vector这类自动内存管理的数据结构。
下面看一个简单的例子:

int main(){int buf[100] = {0};
//下面没有做准确的边界检查
for (int num = 0;num <= 100;num++){buf[num] = num;
}
//可以用vector替代
std::vector<int> vec;
for (int num = 0;num <= 100;num++){vec.emplace_back(num);
}return 0;
}

2、释放后应用
use after free,释放后应用。这个就属于看上去简单,其实真正应用起来就比较麻烦了。大家可能觉得,我不在应用释放掉的指针不就可以了?确实如此。但实际情况非常复杂,既有可能自己疏忽大意,也有可能是别人传入的指针被意外释放但没有置空,也有可能是多线程中的锅,凡此种种,不一而足。
常见的这种内存问题引起的原因有以下几种,看看自己犯过哪类:
1)野指针或悬垂指针:这种一般见于初学者,就是把指针释放后又能各种情况供其它模块使用,或者参数及返回值等情况传递给相关调用者(比如局部变量的指针)。
2)重复释放指针:这个一般是疏忽或者见于接口设计不好,互操作异常引起的。
3)提前释放全局或静态指针:这种情况一般也是设计未考虑周全,在某些情况下导致了指针的提前释放。但一般这种设计上都会有置nullptr或NULL的过程,不会引起什么大的问题。
4)多线程间的非安全指针访问:这个就属于比较难缠的情况,正常情况下,应该避免这种设计。如果是必须不可的应用,则要增加同步和判断机制。其实最好是引入更高一层的缓冲加锁控制。
解决这种内存问题同样和上面的溢出一样有几种方式:
1)使用内存工具或编译工具的内存检查选项(同上)
2)使用RAII封装内存应用(有点类似于智能指针)
3)使用智能指针
4)内存在使用完成后,指针置空(供后续检查避免问题)
5)使用内存池

三、说明

内存问题除了上面提到的两种,其实有很多。比如内存被破坏,内存泄露以及内存多线程访问等等。内存的问题一直是各个编程语言的主流的问题,究其原因就在于,内存的管理的复杂度。这个内存管理的复杂度既包含应用层也包含库,实际上也指向上操作系统。
所以说,解决内存问题,不是一个单纯的应用控制的问题,它是一个系统工程。需要开发者不断的提升自己的技术和编程思想,清楚明确的知道自己当前的需求和实际条件,“以无隙入有间”,这才能很好的解决实际问题。
另外,内存引起的问题,可谓千奇百怪。有的时候不光是应用层的锅。记得在网上看过,有人说在一个循环里不断的开辟内存,但内存较大,应用后会回收内存,时间长了,就内存溢出了。原因在于它们的底层使用的tcmalloc。而其有一个特点,使用过的内存一般不会交还给系统,而是为了高效便于再次利用采取自己管理。可当开辟的较大较多的情况下,连续不断的向系统申请较大内存,系统就抗不住了。
所以说,每一个开发者要对自己的代码熟悉,也要对自己的面对的开发环境熟悉。因为对于客户或者测试来说,不管是谁的锅,都得先甩给你。总之,就是一直提倡的大原则,要根据实际情况,实事求是的来处理问题。这就需要有扎实的理论功底和丰富的实践经验。

四、总结

要想尽量减少内存问题,不但要有足够的知识和技术,更重要的要关于利用工具。当然,调试的水平也要不断的提高。多管齐下,才是解决问题的好办法。千万条路去罗马,总有一条适合开发者自己。

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

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

相关文章

【网络协议栈】Tcp协议(上)结构的解析 和 Tcp中的滑动窗口(32位确认序号、32位序号、4位首部长度、6位标记位、16为窗口大小、16位紧急指针)

绪论​ “没有那么多天赋异禀&#xff0c;优秀的人总是努力翻山越岭。”本章主要讲到了再五层网络协议从上到下的第二层传输层中使用非常广泛的Tcp协议他的协议字段结构&#xff0c;通过这些字段去认识其Tcp协议运行的原理底层逻辑和基础。后面将会再写一篇Tcp到底是通过什么调…

【保姆级】Spring Retry 教程

什么是“重试”?为什么要进行“重试”呢? “重试”(Retry)是一种在编程和软件开发中常见的策略,用于处理在执行操作时可能遇到的临时性错误或异常。当一个操作因为某些原因(如网络问题、服务不可用、资源暂时不可用等)失败时,重试机制会尝试再次执行该操作,以期在下一…

PHP发票验真 API-发票真伪查验、验证接口示例

发票验真API是一种在线服务&#xff0c;它允许用户或企业通过编程接口&#xff08;API&#xff09;验证发票的真实性。这种服务通常由政府机构或者授权的第三方&#xff08;如翔云、百度&#xff09;提供&#xff0c;旨在打击伪造发票的行为&#xff0c;保护消费者的合法权益&a…

JAVA-石头迷阵小游戏

采用企业式项目结构,接下来我将分享全部代码和结构,希望大家点点关注! 这是我的结构。首先使用IDE创建一个Module,命名stone-maze,接着把自带src下的main方法删除,接着在src下创建包,包名为com.wmuj,接着创建APP类代码如下: package com.wmuj;public class App {publ…

《探索 Python 音频利器:sounddevice》

一、sounddevice 简介 Sounddevice 是一个强大的 Python 音频处理库&#xff0c;它为开发者提供了对 PortAudio 库的 Python 绑定&#xff0c;从而实现了在 Python 环境中播放和录制音频数据的功能。 这个库具有诸多优势。首先&#xff0c;它具有跨平台性&#xff0c;无论是在…

进程间通信大总结Linux

目录 进程间通信介绍 进程间通信目的 进程间通信发展 进程间通信分类 管道 System V IPC POSIX IPC 管道 什么是管道 匿名管道 用fork来共享管道原理 站在文件描述符角度-深度理解管道 管道读写规则 管道特点 命名管道 创建一个命名管道 匿名管道与命名管道的区…

RabbitMQ系列学习笔记(八)--发布订阅模式

文章目录 一、发布订阅模式原理二、发布订阅模式实战1、消费者代码2、生产者代码3、查看运行结果 本文参考&#xff1a; 尚硅谷RabbitMQ教程丨快速掌握MQ消息中间件rabbitmq RabbitMQ 详解 Centos7环境安装Erlang、RabbitMQ详细过程(配图) 一、发布订阅模式原理 在开发过程中&…

linux查看系统类型

要确定系统是 Ubuntu 还是 CentOS&#xff0c;可以通过查看系统的发行版信息来判断。以下是几种常见的方法&#xff1a; 方法一&#xff1a;使用 cat 命令查看 /etc/os-release 文件 这个文件包含了系统的详细信息&#xff0c;包括发行版名称和版本号。 cat /etc/os-release…

ESP32-C3实现非易失变量(Arduino IDE )

1效果 网页输入数据&#xff0c;串口打印数据。掉电后数据还在 2源码 #include <WiFi.h> // 包含WiFi库&#xff0c;用于处理WiFi连接 #include <WebServer.h> // 包含WebServer库&#xff0c;用于创建Web服务器 #include <Preferences.h> // 包含Prefere…

告别微信封号!学会这5招,让你的账号坚不可摧

在这个信息爆炸的时代&#xff0c;无论是工作沟通、社交互动还是获取信息&#xff0c;微信都扮演着极其重要的角色。但是&#xff0c;随着微信平台规则的日益严格&#xff0c;账号被封的风险也随之增加。今天&#xff0c;我们就来聊聊如何有效防止 微信被封&#xff0c;让你的账…

【MySQL】入门篇—基本数据类型:NULL值的概念

在关系数据库中&#xff0c;NULL值是一个特殊的标记&#xff0c;表示缺失或未知的值。 NULL并不等同于零&#xff08;0&#xff09;或空字符串&#xff08;&#xff09;&#xff0c;它表示一个字段没有任何值。 这一概念在数据库设计和数据管理中至关重要&#xff0c;因为它影…

力扣——环形链表问题

判断链表是否有环以及入环的第一个节点 前言判断链表是否有环找到入环的第一个节点 前言 大家好&#xff0c;前段时间&#xff0c;熊二学习了关于环形链表相关的问题&#xff0c;以下是我的见解&#xff0c;希望能够帮助你们呀&#xff01; 判断链表是否有环 给定一个链表&am…

如何在一个月内快速学习掌握大模型

原本给自己的是一个月时间&#xff0c;通过梳理之后我自信的认为不需要一个月&#xff0c;两周即可&#xff0c;相较于其他技术&#xff0c;大模型应用的门槛要低得多。 先明确你想要深入到哪一层 1、基础设施层&#xff1a;了解即可&#xff0c;关注NVIDIA和超大规模厂商的最…

[自动化测试:Selenium]:环境部署和Webdriver的使用

文章目录 修改安装源打开Python Packages。点击梅花按钮。在弹出的对话框中&#xff0c;填入Name&#xff08;随便填&#xff09;&#xff0c;Repository URL&#xff0c;选择下列的源&#xff0c;一般先选择清华源按OK确认。配置完成 安装seleniumFile→Settings→Project&…

多签机制简明理解及实例说明

目录 Multisignature机制简明理解及实例说明 Multisignature机制中的公钥、私钥、Nonce及签名验签详解 加密货币托管账户的多重签名机制 Multisignature机制简明理解及实例说明 一、基本概念 Multisignature(多重签名)机制是一种先进的加密技术,它允许一笔交易必须由多…

word删除空白页 | 亲测有效

想要删掉word里面的末尾空白页&#xff0c;但是按了delete之后也没有用 找了很久找到了以下亲测有效的方法 1. 通过鼠标右键在要删除的空白页面处显示段落标记 2. 在字号输入01&#xff0c;按ENTER&#xff08;回车键&#xff09; 3.成功删除了&#xff01;&#xff01; PS…

【ArcGIS Pro实操第八期】绘制WRF三层嵌套区域

【ArcGIS Pro实操第八期】绘制WRF三层嵌套区域 数据准备ArcGIS Pro绘制WRF三层嵌套区域Map-绘制三层嵌套区域更改ArcMap地图的默认显示方向指定数据框范围 Map绘制研究区Layout-布局出图 参考 本博客基于ArcGIS Pro绘制WRF三层嵌套区域&#xff0c;具体实现图形参考下图&#x…

两性离子水凝胶助力微针传感器:稳定灵敏的监测神器

大家好&#xff01;今天我要向大家介绍一项关于生物相容性核壳微针传感器的研究——《Biocompatible Core–Shell Microneedle Sensor Filled with Zwitterionic Polymer Hydrogel for Rapid Continuous Transdermal Monitoring》发表于《ACS Nano》&#xff0c;该传感器填充两…

力扣每日打卡挑战 3184. 构成整天的下标对数目 I

给你一个整数数组 hours&#xff0c;表示以 小时 为单位的时间&#xff0c;返回一个整数&#xff0c;表示满足 i < j 且 hours[i] hours[j] 构成 整天 的下标对 i, j 的数目。 整天 定义为时间持续时间是 24 小时的 整数倍 。 例如&#xff0c;1 天是 24 小时&#xff0c…

深入解析 Golang 并发编程中的同步机制:WaitGroup 与 Mutex 详解

文章目录 一、简介二、WaitGroup 的使用1. 什么是 WaitGroup&#xff1f;2. 基本操作3. WaitGroup 示例4. 注意事项 三、Mutex 的使用1. 什么是 Mutex&#xff1f;2. 基本操作3. Mutex 示例 四、竞争条件示例与解决1. 竞争条件问题示例2. 使用 Mutex 解决竞争条件 五、使用 RWM…