使用 STL 容器发生异常的常见原因分析与总结

目录

1、概述

2、使用STL列表中的元素越界

3、遍历STL列表删除元素时对迭代器自加处理有问题引发越界

4、更隐蔽的遍历STL列表删除元素时引发越界的场景

5、多线程同时操作STL列表时没有加锁导致冲突

6、对包含STL列表对象的结构体进行memset操作导致STL列表对象内存出异常

7、最后


VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124272585C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931C++软件分析工具从入门到精通案例集锦(专栏文章正在更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131405795C/C++基础与进阶(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.html开源组件及数据库技术(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_2276111.html       有了STL容器之后,我们基本不再需要再去设计链表等数据结构了,STL中的vector、list、map等容器基本就能满足我们的需求了。但我们在使用STL列表可能会因为编码不规范遇到这样那样的问题,导致软件出现异常,特别是新人会因为缺乏经验容易犯一些错误。正好前段时间在技术群中有粉丝就遇到使用STL容器的问题,于是问我可能是哪些原因导致的。在此,我结合以前项目中遇到的多个问题场景,给大家做个详细的总结,以供借鉴或参考。

1、概述

       在项目代码中,我们会大量地使用C++标准库给我们提供的vector、list、map等STL标准模板库(Standard Template Library)容器。这些使用模版实现的容器列表很灵活、很好用,给我们日常编码带来了很大的便利。但在使用STL列表时可能会因为经验不足遇到这样或那样的问题。

       本文根据多年的项目问题排查经验以及遇到的多个问题场景,大概地总结一下操作STL列表时发生异常的常见原因,如下:

1)使用数组下标(仅能操作vector)或者使用迭代器直接去访问STL列表中的元素,但这些元素不存在,导致越界访问。一般是因为没有判断STL列表为空,或者没有判断要访问的元素是否存在。
2)在遍历STL列表删除元素时迭代器自加处理的有问题。
3)多线程同时操作STL列表时没有加锁保护,导致访问冲突。一般是一个线程在遍历STL列表,另一个线程在增删STL列表中的元素,导致访问冲突,出现异常崩溃。
4)结构体某个成员是STL列表对象,对结构体对象进行memset操作,破坏了STL内部的内存结构,导致访问STL列表时出异常。大家在定义结构体对象时,习惯性地进行了memset操作。但能进行memset的前提是,结构体中所有成员都必须是基本类型的!一旦结构体中包含C++类对象,是严禁进行memset操作的。


         在这里,给大家重点推荐一下我的几个热门畅销专栏:

专栏1:(该专栏订阅量已达到400个,有很强的实战参考价值,广受好评!专栏文章持续更新中,预计更新到200篇以上!)

C++软件调试与异常排查从入门到精通系列文章汇总icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931

本专栏根据近几年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法,详细讲述了C++软件的调试方法与手段,以图文并茂的方式给出具体的实战问题分析实例,带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!

专栏中的文章均是通过项目实战总结出来的(通过项目实战积累了大量的异常排查素材和案例),有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到200篇以上!

专栏2: 

C/C++基础与进阶(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.html

以多年的开发实战为基础,总结并讲解一些的C/C++基础与进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域的多个方面的内容,同时给出C/C++及网络方面的常见笔试面试题,并详细讲述Visual Studio常用调试手段与技巧!

专栏3: 

开源组件及数据库技术icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12458859.html

以多年的开发实战为基础,分享一些开源组件及数据库技术!


2、使用STL列表中的元素越界

       比如我们在使用vector列表时,常使用数组下标的方式去访问列表中的元素,有时可能没有判断列表中元素个数,直接就使用下标去访问了,比如:

// 设备信息
struct TDeviceInfo
{char szDeviceId[64];   // 设备idchar szDeviceName[64]; // 设备名称char szIp[32];         // 设备ipunsigned short uPort;  // 设备端口int nDevType;          // 设备类型
};vector<TDeviceInfo> vtDevList;
int nDevType = vtDevlist[3].nDevType;

可能当前列表vtDevlist中的元素个数少于3个,但我们用数组下标的方式去强行访问了,导致Out of range访问越界了。

我们之所以通过数组下标去访问vector列表元素,不会平白无故地去访问,而是与我们的业务场景关联的。

       还有一种情况是使用迭代器去越界访问的

vector<TDeviceInfo>::iterator iter = vtDevList.begin();
int nDevType = iter->nDevType;

没有判断列表是否为空,强行去访问列表中的begin元素的。没有判断当前迭代器是否为end的,即

if (itor != vtDevList.end() )
{
}

这个问题场景,我们在项目中多次遇到过。

3、遍历STL列表删除元素时对迭代器自加处理有问题引发越界

       这是新人比较容易犯的错误。比如删除设备列表中设备类型等于1的所有设备,有问题的代码如下:

vector<TDeviceInfo> vtDevList;
vector<TDeviceInfo>::iterator iter = vtDevList.begin();
for (; iter != vtDevList.end(); iter++)
{if (iter->nDevType == 1){vtDevList.erase(iter);}
}

如果有元素被删除,在Debug下调试时,会报如下的错误:

       正确的写法是:

for (; iter != vtDevList.end(); )
{if (iter->nDevType == 1){iter = vtDevList.erase(iter);}else{iter++;}
}

当执行erase操作后,迭代器iter原先的指向就失效了,不能再对其进行++操作,否则就越界了。对于迭代器变量iter,要分开处理,如果有删除元素,则将erase接口返回的迭代器赋值给iter;如果没有删除元素,直接对迭代器进行++操作。

新人在写遍历STL列表删除元素的代码时可能会犯上面的错误,没有区分对待。

4、更隐蔽的遍历STL列表删除元素时引发越界的场景

        这个问题场景和上面讲的遍历STL列表删除元素的场景类似,但本场景更有隐蔽性,这在以前的项目中遇到过。

这个问题是同事那边遇到的,在调试程序时老是在遍历STL列表时出现异常,于是找我过去帮忙排查一下。

       问题场景是这样子的,在遍历STL列表的for循环体中调了一个函数fun1,因为业务比较复杂,涉及了多个函数的调用,fun1函数内部调用了fun2函数,fun2函数中调了fun3函数,fun3函数中调用了fun4函数,问题就出在fun4函数中,fun4函数中居然将当前最上面正在遍历的STL列表中的元素删除了,演示代码如下:

vector<TDeviceInfo> vtDevList;
vector<TDeviceInfo>::iterator iter = vtDevList.begin();
for (; iter != vtDevList.end(); iter++)
{// 调用fun1fun1();
}void fun2()
{// 调用fun3fun3();
}void fun3()
{// 调用fun4fun4();
}void fun4()
{// 此处删除了STL列表vtDevList中的内容
}

从而出现在遍历STL列表时删除列表中元素的场景。应该像上面那个场景中那样,处理代码:

for (; iter != vtDevList.end(); )
{if (iter->nDevType == 1){iter = vtDevList.erase(iter);}else{iter++;}
}

但实际上,因为当前问题场景中函数调用层次很深,不适合这样处理,只能将这种在遍历STL列表时删除元素的操作给取缔掉,重新编写代码控制逻辑。正是因为函数调用层次比较深,所以这个问题有很强的隐蔽性。

当时排查这个问题时,凭着经验,首先排除多线程同时操作同一个STL列表的场景,当时觉得大概率是遍历STL列表过程中有地方删除了STL列表中的元素导致的。然后沿着这个思路,最终在某个函数中找到了删除STL列表元素的代码。

5、多线程同时操作STL列表时没有加锁导致冲突

        多线程同时操作某个STL列表时产生问题的典型场景是,一个线程在遍历STL列表,另一个线程在对STL列表中的元素进行增删操作。一般在多线程操作共享资源时都要加锁,但加锁有个原则,尽量较小锁的范围,在锁的范围的代码要尽快执行完,不能影响其他线程的执行效率。

       这个问题,我们在项目中遇到不止一次了。有时可能比较具体隐蔽性,比如在另一个线程中调用某个函数接口,在该函数内部操作了一个公用的STL列表,然后出现了多线程同时操作STL列表的场景,有时较难发现这里面隐藏的多线程操作共享资源的问题,特别是在维护他人编写的代码模块时。

6、对包含STL列表对象的结构体进行memset操作导致STL列表对象内存出异常

       大家在定义结构体对象时,习惯性地在使用之前对结构体对象进行了memset操作。如果结构体中某个成员是STL列表对象,执行memse操作后,会破坏STL对象内部的内存结构,导致访问STL列表时出现异常。这个问题在项目中出现过,当时是一个新人写的代码(新人考虑问题不够全面,缺乏安全意识),因为缺乏经验,直接对一个包含STL列表对象的结构体进行memset操作,比如:(结构体TWindowInfo中包含了一个STL list成员)

typedef struct tagWindowInfo
{
public:HWND hWnd;                               // 当前窗口句柄char szWndName[256];                     // 当前窗口名称RECT m_rcWnd;                            // 当前窗口矩形坐标(在屏幕坐标系中)std::list<tagWindowInfo> childWndList;   // 当前窗口所有子窗口信息列表
}TWindowInfo;// 结构体TWindowInfo中包含了一个list列表成员,下面直接对结构体对象执行了memset操作
TWindowInfo tWndInfo;
memset(&tWndInfo, 0, sizeof(tWndInfo));	

导致程序在访问这个STL列表时出现了各种莫名其妙的错误,比如列表中的元素数据不对,执行STL列表类的成员函数发生异常崩溃。当时这个问题我查了很久,后来才发现是因为对结构体进行memset操作导致的。

      注意,能进行memset的前提是,结构体中所有成员都必须是基本类型的!一旦结构体中包含C++类对象,是严禁进行memset操作的。为什么不能对类对象进行memset操作呢?比如类中有虚函数,就会内置一个虚函数表指针,这个虚函数表指针中存放的是存放虚函数地址(代码段地址)的虚函数表,在调用虚函数时会用到这个虚函数表。如果对类对象进行memset操作,会将虚函数表指针中的值置为NULL,这样在调用虚函数时出现问题。对于虚函数调用的过程与机制,需要从汇编代码的角度去看,涉及到两次寻址,具体过程可以参见我之前写的文章:
几秒读懂C++虚函数调用的汇编代码实现icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/121046234       再比如,有些类实现的比较复杂,例如STL列表,类内部会有额外的变量去维护管理较复杂的内部内存结构,如果对这样的类对象进行memset操作,将维护内部内存结构的变量的值都只为NULL了,结果是不可预料(unexpected)的。 

         对STL列表执行memset操作引发异常的问题,我们在项目中遇到了两次,都是新人写的代码,两次的表现现象是不太一样的:

1)一次是在遍历STL列表读取列表中元素发现元素值异常。

2)一次是操作STL列表时产生了异常,直接将当前函数余下的代码都跳过了,导致该执行的代码没有执行,导致后续代码执行时逻辑出现异常。

       其实C++中的大部分问题都是内存问题,在分析这些问题时要从内存的角度去看。很多问题,从内存的角度去看,就好理解多了!变量本质上就是一块内存,给变量赋值就是向内存中写入内容,读变量的值就是读取内存中的内容,这一点从汇编代码上可以清晰地看出来!

7、最后

       在分析和解决问题时,大家要主动地多想想多思考,多倒腾倒腾里面的细节问题,必要的时候可以进行归纳与总结。很多细节知识点,都是相通的,搞懂一个细节,相关的细节点可能很快就能明白了!   

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

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

相关文章

2024蓝桥杯每日一题(树状数组2)

备战2024年蓝桥杯 -- 每日一题 Python大学A组 试题一&#xff1a;数星星 试题二&#xff1a;小朋友排队 试题三&#xff1a;逆序对数量 试题四&#xff1a;火柴排队 试题一&#xff1a;数星星 【题目描述】 天空中有一些星星&#xff0c;这些星星都在…

3.18_C++_day6_作业

作业要求&#xff1a; 程序代码&#xff1a; #include <iostream>using namespace std;class Animal { private:string name;string color;int *age; public://无参构造函数Animal(){cout << "Animal::无参构造函数" << endl;}//有参构造函数Anim…

【计算机】——51单片机——持续更新

单片机是一种内部包含CPU、存储器和输入/输出接口等电路的集成电路&#xff08;IC芯片&#xff09; 单片机是单片微型计算机&#xff08;Single Chip Microcomputer&#xff09;的简称&#xff0c;用于控制领域&#xff0c;所以又称为微型控制器&#xff08;Microcontroller U…

Python零基础---爬虫技术相关

python 爬虫技术&#xff0c;关于数据相关的拆解&#xff1a; 1.对页面结构的拆解 2.数据包的分析&#xff08;是否加密了参数&#xff09;&#xff08;Md5 aes&#xff09;难易程度&#xff0c;价格 3.对接客户(433,334) # 数据库 CSV 4.结单&#xff08;发一部分数据&a…

服务器运行一段时间后

自己记录一下。 一、查看目录占用情况 df -h 命令查看磁盘空间 du -ah --max-depth=1 / 查看根目录下各个文件占用情况 二、mysql日志清空 这个日志是可以清空的 echo > /usr/local/mysql/data/syzl-db2.log #将文件清空 说明: 这个文件这么大是因为,开启 …

Leetcode热题100:图论

Leetcode 200. 岛屿数量 深度优先搜索法&#xff1a; 对于这道题来说&#xff0c;是一个非常经典的图的问题&#xff0c;我们可以先从宏观上面来看问题&#xff0c;也就是说在不想具体算法的前提下&#xff0c;简单的说出如何找到所有的岛屿呢&#xff1f; 如图中所示&#x…

初识React(一)从井字棋游戏开始

写在前面&#xff1a; 磨磨唧唧了好久终于下定决心开始学react&#xff0c;刚刚接触感觉有点无从下脚...新的语法新的格式跟vue就像两种物种...倒是很好奇路由和store是怎么实现的了~v~&#xff0c;一点一点来吧&#xff01;&#xff01;&#xff01; (一)创建项目 使用vite…

FreeRtos学习笔记(12)systemView 分析任务调度情况

FreeRtos学习笔记&#xff08;12&#xff09;systemView 分析任务调度情况 使用stm32f429 freertosV10.5.1 systemView 3.5 keil AC5 systemView 移植 从官网下载 systemView 软件 将下面文件添加到工程中 freertos 修改 systemView 需要 FreeRTOSConfig.h 开启如下宏, …

Affinity Photo:像素大师,影像重塑者 mac/win版

在数字图像处理领域&#xff0c;Affinity Photo已经崭露头角&#xff0c;成为许多专业摄影师和图像设计师的首 选工具。这款软件不仅具备丰富的功能和强大的性能&#xff0c;还提供了直观易用的操作界面&#xff0c;让用户能够轻松实现高质量的图像处理。 Affinity Photo软件获…

如何在iOS系统抓取log

前言&#xff1a;因为作者目前工作领域和苹果智能家居有关&#xff0c;然后发现一些bug其实是apple sdk原生code的问题&#xff0c;所以需要给apple提radar单&#xff0c;就需要抓ios端Log充当证据给apple看&#xff0c;其实ios抓log非常简单&#xff0c;大家感兴趣可以学习下哦…

众邦科技CRMEB商城商业版任意文件写入getshell 0day

代码审计 接口&#xff1a;/adminapi/system/crud 处理的代码如下 public function save(SystemCrudDataService $service, $id 0){$data $this->request->postMore([[pid, 0],//上级菜单id[menuName, ],//菜单名[tableName, ],//表名[modelName, ],//模块名称[table…

NAT---网络地址转换技术

Network Address Translation 1、起源&#xff1a;ip地址不够用 2、作用&#xff1a;让私网地址映射成公网地址&#xff0c;进而访问网络。 3、私网Ip地址的范围&#xff1a; A类&#xff1a;10.0.0.0-10.255.255.255 B类&#xff1a;172.16.0.0-172.31.255.255 C类&…

论文精读 | 2024 [ICLR] TimeMixer: 可分解多尺度融合的时间序列预测

论文标题&#xff1a;TimeMixer: Decomposable Multiscale Mixing for Time Series Forecasting 作者&#xff1a;Shiyu Wang&#xff08;王世宇&#xff09;, Haixu Wu&#xff08;吴海旭&#xff09;, Xiaoming Shi, Tengge Hu, Huakun Luo, Lintao Ma, James Y. Zhang, and…

【进阶五】Python实现SDVRP(需求拆分)常见求解算法——禁忌搜索+模拟退火算法(TS+SA)

基于python语言&#xff0c;采用经典禁忌搜索&#xff08;TS&#xff09;模拟退火&#xff08;SA&#xff09;对 需求拆分车辆路径规划问题&#xff08;SDVRP&#xff09; 进行求解。 目录 往期优质资源1. 适用场景2. 代码调整3. 求解结果3.1 TS3.2 SA 4. 代码片段参考 往期优质…

MySQL索引(图文并茂)

目录 一、索引的概念 二、索引的作用 三、创建索引的原则依据 四、索引的分类和创建 1、索引的分类 2、索引的创建 2.1 普通索引 2.1.1 直接创建索引 2.1.2 修改表方式创建 2.1.3 创建表的时候指定索引 2.2 唯一索引 2.2.1 直接创建唯一索引 2.2.2 修改表方式创建 …

C 多维数组

C 语言支持多维数组。多维数组声明的一般形式如下&#xff1a; type name[size1][size2]...[sizeN];例如&#xff0c;下面的声明创建了一个三维 5 . 10 . 4 整型数组&#xff1a; int threedim[5][10][4];二维数组 多维数组最简单的形式是二维数组。一个二维数组&#xff0c…

每秒批量插入10000条数据到MySQL中,资源消耗(带宽、IOPS)有多少?

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容起因代码资源情况改造 &#x1f4e2;文章总结&#x1f4e5;博主目标 &#x1f50a;博主介绍 &#x1f31f;我是廖志伟&#xff0c;一名Java开发工程师、Java领域优质创作者、CSDN博客专家、51CTO专家博主、阿里云专家博主、…

Redis入门到实战-第十二弹

Redis实战热身Bitfields篇 完整命令参考官网 官网地址 声明: 由于操作系统, 版本更新等原因, 文章所列内容不一定100%复现, 还要以官方信息为准 https://redis.io/Redis概述 Redis是一个开源的&#xff08;采用BSD许可证&#xff09;&#xff0c;用作数据库、缓存、消息代理…

Elasticsearch:使用在本地计算机上运行的 LLM 以及 Ollama 和 Langchain 构建 RAG 应用程序

无需 GPU 的隐私保护 LLM。在本博客中&#xff0c;我将演示使用不同的工具 Ollama 构建的 RAG 应用程序。 与本文相关的所有源代码均已发布在 github上。 请克隆存储库以跟随文章操作。我们可以通过如下的方式来克隆&#xff1a; git clone https://github.com/liu-xiao-guo/o…

Unity 学习笔记 5.控制飞机飞行

目录 1.摄像机跟随的方法 2.鼠标按键响应 3.键盘按键响应 4.导入素材 5.让飞机向前飞 6.摄像机跟随飞机移动 7.鼠标控制飞机倾斜 8.键盘控制飞机飞行 下载源码 UnityPackage 1.摄像机跟随的方法 2.鼠标按键响应 3.键盘按键响应 4.导入素材 下载素材 步骤&#xff1a; 将…