0xcdcdcdcd异常值引发C++程序崩溃问题的详细分析

news2024/10/5 13:46:21

目录

1、0xcccccccc、0xcdcdcdcd和0xfeeefeee等常见异常值的说明

2、由0xcdcdcdcd 异常值引发的内存访问违例问题说明

2.1、用户态内存地址与内核态用户地址

2.2、根据0xcdcdcdcd异常值初步估计出引发问题的原因

3、详细分析与问题解决

4、变量未初始化在Debug和Release下的差异

5、最后


VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=M85Bhttps://blog.csdn.net/chenlycly/article/details/124272585C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=M85Bhttps://blog.csdn.net/chenlycly/article/details/125529931        以前在总结Visual Studio高效调试手段与技巧时,特别提到了对0xcccccccc、0xcdcdcdcd和0xfeeefeee等常见异常值的辨识度,当时有些人可能对这点不以为然。今天我们就来通过一个问题实例来讲述一下这点的重要性。如果在调试过程中遇到这些异常值,通过这些特殊值的具体含义,也许就能找到排查问题的线索,并最终定位问题。

1、0xcccccccc、0xcdcdcdcd和0xfeeefeee等常见异常值的说明

        0xcccccccc 、0xcdcdcdcd 和0xfeeefeee 等常见异常值的说明如下所示:

* 0xcccccccc : Used by Microsoft's C++ debugging runtime library to mark uninitialised stack memory

* 0xcdcdcdcd : Used by Microsoft's C++ debugging runtime library to mark uninitialised heap memory

* 0xfeeefeee : Used by Microsoft's HeapFree() to mark freed heap memory

* 0xabababab : Used by Microsoft's HeapAlloc() to mark "no man's land" guard bytes after allocated heap memory

* 0xabadcafe : A startup to this value to initialize all free memory to catch errant pointers

* 0xbaadf00d : Used by Microsoft's LocalAlloc(LMEM_FIXED) to mark uninitialised allocated heap memory

* 0xbadcab1e : Error Code returned to the Microsoft eVC debugger when connection is severed to the debugger

* 0xbeefcace : Used by Microsoft .NET as a magic number in resource files

        对于0xcccccccc 和0xcdcdcdcd,在 Debug 模式下,VS会把未初始化的栈内存全部填充成0xcccccccc ,当成字符串看就是“烫烫烫烫……”;VS会把未初始化的堆内存全部填充成 0xcdcdcdcd ,当成字符串看就是 “屯屯屯屯……”。这两类特殊的字符串,很多人应该都见到过。

那么调试器为什么要这么做呢?VS在Debug下把未初始化的内存自动填充成0xcccccccc 或0xcdcdcdcd ,而不是就让取随机值,是为了方便我们快速识别出问题,如果内存中出现这两个值,很可能就是因为变量没有初始化导致的。如果内存中的值是随机的,那么每次调试程序时就可能出现不一样的结果,比如这次程序崩掉,下次却能正常运行,这样显然对我们解bug是非常不利的。

        如果在Debug调试时遇到有变量的值为0xcccccccc 或0xcdcdcdcd ,一般是变量没有初始化引起的。没有初始化的栈变量(在栈上分配内存的变量)的内存会被初始化为0xcccccccc ,没有初始化的堆变量(在堆上分配内存的变量)的内存会被初始化为0xcdcdcdcd 。

        对于0xfeeefeee ,是用来标记堆上已经释放掉的内存,即已经释放的堆内存会被填充成0xfeeefeee 。

注意,如果指针指向的内存被释放了,指针变量本身的地址是没做改动的,还是之前指向的内存的地址,只是指向的堆内存会被填充成0xfeeefeee 。

如果该指针是一个类的成员变量,并且该类对象是在堆上分配内存的,则该类对象的堆内存被释放后(对于C++类,通常是执行delete操作),类对象中的指针变量就会被赋值为0xfeeefeee 。如果在调试代码过程中,发现指针的值为0xfeeefeee ,就说明该指针所在类对象的内存被释放掉了,如果继续使用该指针变量,可能就会出问题了。我们在日常调试时会经常遇到0xfeeefeee 这个异常值的。

2、由0xcdcdcdcd 异常值引发的内存访问违例问题说明

        新人在VS中对代码进行Debug调试,调试某个新功能时会时不时遇到崩溃,相关信息如下所示:

这个崩溃不是必现的,但复现的概率很大。

2.1、用户态内存地址与内核态用户地址

        根据提示信息得知,是0xcdcdcdcd 异常值引发的内存访问违例的崩溃,崩溃处的类对象的this值为0xcdcdcdcd。这个地址值对于32位程序来说,其大于0x80000000,属于内核态的地址范围,用户态的代码是禁止访问内核态内存地址的,所以触发了内存访问违例。

一般情况下,软件中的业务模块都是运行在用户态中的,进程中加载的系统内核模块是运行进程的内核态中的,比如驱动程序就是运行在内核态的。

        那到底哪些地址范围是属于用户态的,哪些内存地址范围是属于内核态的呢?以Windows中的32位程序程序为例,系统会给32程序进程分配4GB的虚拟地址空间,一般情况下,32位程序进程的内核态和用户态各占一半(各占2GB),即用户态的内存地址范围为:0x00000000 - 0x7FFFFFFF,内核态的内存地址范围为:0x80000000 - 0xFFFFFFFF。

        关于不同系统模式下的内核态和用户态内存的划分,可以参加《Windows核心编程》一书的第13章关于Windows虚拟地址空间如何分区一节中的说明:

在Linux系统中,32位程序的4GB总的虚拟内存空间,用户态占3GB,内核态占1GB。

2.2、根据0xcdcdcdcd异常值初步估计出引发问题的原因

        对于0xcdcdcdcd ,其含义为: Used by Microsoft's C++ debugging runtime library to mark uninitialised heap memory,即微软C++调试运行时库会将没有初始化的堆内存都置为0xcdcdcdcd 。说明该处的CWindowWnd指针是没有初始化的,然后到call stack窗口中查看函数调用堆栈,看看是哪个函数使用了没有初始化的CWindowWnd*指针的。

3、详细分析与问题解决

       上述崩溃问题,是在VS中调试代码时遇到的,于是切换到call stack函数调用堆栈页面,查看到如下的函数调用堆栈:

从函数调用堆栈中可以看出,是CRtcFullVideoWndUI::OnUpdateBigVideoCursor函数调用了发生异常的代码,在CRtcFullVideoWndUI::OnUpdateBigVideoCursor函数中用到了CSmallVideoWndUI* m_pBigVideoChildWnd成员变量,其中CSmallVideoWndUI类是继承于CWindowWnd的。

        上述发生崩溃的C++对象就是m_pBigVideoChildWnd指针变量指向的对象。
根据0xcdcdcdcd异常值的含义, 可以推测,在CRtcFullVideoWndUI::OnUpdateBigVideoCursor函数中使用到CSmallVideoWndUI* m_pBigVideoChildWnd变量时,该变量没有初始化。

        给CSmallVideoWndUI* m_pBigVideoChildWnd变量赋值的操作位于CRtcFullVideoWndUI::UpdateRtcBigVideo函数中,如下所示:

void CRtcFullVideoWndUI::UpdateRtcBigVideo()
{
    ::SetParent(m_pToolBarWnd->GetHWND(), m_hWnd);
    ::SetParent(m_pStatusBarWnd->GetHWND(), m_hWnd);

    ::DestroyWindow(m_pBigVideoChildWnd->GetHWND());
    m_pBigVideoChildLay->SetAutoDestroy(false);
    m_pBigVideoChildLay->RemoveAt(0);
    m_pBigVideoChildLay->SetAutoDestroy(true);

    m_pBigVideoChildWnd = new CSmallVideoWndUI();
    m_pBigVideoChildWnd->SetBigVideo(true);
    m_pBigVideoChildWnd->SetWndColor(0xFF000000);
    m_pBigVideoChildWnd->SetFixedHeight(m_pRtcVideoParentLay->GetBigVideoHeight());
    m_pBigVideoChildWnd->SetFixedWidth(m_pRtcVideoParentLay->GetBigVideoWidth());
    m_pBigVideoChildWnd->SetName(_T("SmallVideoWnd"));
}

在本例的崩溃场景中,运行到CRtcFullVideoWndUI::OnUpdateBigVideoCursor函数中时,上述给CSmallVideoWndUI* m_pBigVideoChildWnd变量赋值的函数CRtcFullVideoWndUI::UpdateRtcBigVideo还没执行到,即CRtcFullVideoWndUI::OnUpdateBigVideoCursor函数先于CRtcFullVideoWndUI::UpdateRtcBigVideo函数执行。

       这个地方牵涉到一个老生常谈的问题,定义变量时一定要给变量初始化。很多人没有初始化变量的习惯,结合以往的问题排查案例,不少问题都是由于变量未初始化引起的。

        为了解决本问题,需要先将CSmallVideoWndUI* m_pBigVideoChildWnd变量的初值赋为NULL,放在CSmallVideoWndUI的构造函数中处理即可;然后在CRtcFullVideoWndUI::OnUpdateBigVideoCursor函数中使用CSmallVideoWndUI* m_pBigVideoChildWnd指针变量之前,判断该指针变量是否为空,代码如下所示:

bool CRtcFullVideoWndUI::OnUpdateBigVideoCursor(WPARAM wParam, LPARAM lParam, bool & bHandled)
{
    if ( m_pBigVideoChildWnd != NULL )
    {
        ::PostMessage(m_pBigVideoChildWnd->GetHWND(), WM_LBUTTONUP, 0, 0);
        ::PostMessage(m_pBigVideoChildWnd->GetHWND(), WM_MOUSELEAVE, 0, 0);
    }
    
    bHandled = true;
    return true;
}

4、变量未初始化在Debug和Release下的差异

        对于Visual C++编译器,Debug下会将栈内存自动初始化为0xcccccccc ,会将堆内存自动初始化为0xcdcdcdcd。但Release下不会对栈内存和堆内存做任何的初始化,此时未初始化的变量的内存中的内容是分配内存时分配到的内存的“残留值”,这样未初始化的变量的值就是随机的。

        所以因为变量值未初始化导致的问题,在Debug和Release下的现象可能是不同的,比如Debug下运行没问题但Release下运行有问题或者Release下运行没问题但Debug下运行有问题,这样的问题我们在项目中遇到过不少次了。所以大家平时写代码时,一定要养成初始化变量的好习惯!

5、最后

        一个不起眼的或者难度不大的问题,有时候也值得去推敲和思考的,小问题也能整理出一些有价值的细节点。积小流以成江海,积跬步以至千里!大家在日常工作中一定要多思考多总结,思考总结的多了,经验也就越多了!
    

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

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

相关文章

idea 创建 maven项目报错

问题报错 org.apache.maven.model.validation.DefaultModelValidator: method ‘void ()’ not found 第一次使用Maven创建工程,导入依赖的时候出现报错 idea版本:2021.2 maven版本:3.8.5 看了许多博客说把maven版本降低即可 参考文章&a…

技术栈入门------ElasticSearch

使用ElasticSearch的准备工作 一、在Linux上安装ElasticSearch 1、docker下载elasticSearch和kibana的镜像 docker pull elasticsearch:7.4.2 存储和检索数据 docker pull kibana:7.4.2 可视化检索数据 2、创建目录 mkdir -p /mydata/elasticsearch/config mkdir -p /myd…

[含文档+源码等]微信小程序校园生活小助手+后台管理系统前后分离VUE[包运行成功]

博主介绍:✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 项目名称 [含文档源码等]微信小程序校园生活小助手后台管理系统前后分离VUE[包运行成功] 演示视频 视频去哪了呢?_哔哩哔哩_bilibili 系统介绍 本系…

简单对比一下VRRP和策略路由之间主备切换的差异

前面我们介绍的VRRP(),两台设备之间处于主备关系,也就是说,当主设备故障之后,流量才会切换到备设备上。这种使用方式一定程度上讲,不太符合经济适用的原则,我们前面介绍过链路聚合&a…

Java面试--SpringMVC请求流程

用户发送请求,请求被 SpringMVC 前端控制器(DispatherServlet)捕获;前端控制器(DispatherServlet)对请求 URL 解析获取请求 URI,根据 URI,调用 HandlerMapping;前端控制器…

求最长递增子序列

题目: 给定数组arr,返回arr的最长递增子序列 举例: arr [2,1,5,3,6,4,8,9,7] 返回最长递增子序列为 [1,3,4,8,9] 方法一:复杂度为O(N^2) 1. 生成长度为N的数组 dp,dp[i] 表示以arr[i] 这…

架构师必读 —— 逻辑模型(8)

利用框架整理信息 框架用于明确“整体结构”,也指“通过MECE分析法将整体构成要素进行大分类”。重要的是框架应是MECE分析框架,因为 一旦有遗漏和重复,就会导致混乱。整体构成要素可大致分为 3~7大类。 建议先确认目的后思考框架&#x…

实时单目追踪和稠密建图的算法框架:TANDEM算法

01 概述 最近在研究TANDEM算法,是一个实时单目追踪和稠密建图的算法框架。TANDEM的算法框架可以概括为如下的几个部分: 1、位姿计算----在关键帧实施基于滑窗的像素级集束优化; 2、追踪前端----实施直接稠密图片配准,基于全局模…

如何找回电脑回收站删除的文件, 10种恢复工具方法!

你想取回你的回收站删除的文件吗?无论是图像、视频还是文档,我们的设备都会存储所有内容。您可能不小心删除了数据,或者由于其他原因而消失了。要恢复您的文件,您可以访问技术人员或下载任何最好的文件恢复软件。 如果您选择第二…

怎样把电脑D盘合并到c盘?相邻的分区怎么合并到C盘

因为C盘是重要的系统盘,系统用户设置文件夹,包括各个用户的文档、收藏夹、上网浏览信息、配置文件,不能随意删除。如果C盘空间不足,就会影响到电脑的整体运行速率,会导致电脑卡顿,要想彻底地解决这种情况&a…

HTML期末作业课程设计期末大作业——电影网页制作

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置,有div的样式格局,这个实例比较全面,有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 精彩专栏推荐&#x1f4…

PHP序列化和反序列化漏洞学习

目录 PHP基础知识: 类的结构: 序列化 反序列化 魔术方法 字符串逃逸 PHP基础知识: 类的结构: 常见访问权限修饰符: public:公共的,在类的内部、子类中或者类的外部都可以使用,不受限制;p…

【GO】 K8s 管理系统项目[API部分--Daemonset]

K8s 管理系统项目[API部分–Daemonset] 1. 接口实现 service/dataselector.go // daemonCell type daemonSetCell appsv1.DaemonSetfunc(d daemonSetCell) GetCreation() time.Time {return d.CreationTimestamp.Time }func(d daemonSetCell) GetName() string {return d.Na…

高性能的Java IDE——IntelliJ IDEA v2022.3全新发布

IntelliJ IDEA,是java编程语言开发的集成环境。IntelliJ在业界被公认为最好的java开发工具,尤其在智能代码助手、代码自动提示、重构、JavaEE支持、各类版本工具(git、svn等)、JUnit、CVS整合、代码分析、 创新的GUI设计等方面的功能可以说是超常的。 I…

怎样在电脑上录制ppt课件?如何录制课件讲解视频

怎样在电脑上录制ppt课件?在当前的环境下,大部分学生都是在家进行上网课,本文就给大家分享几款非常简单实用的录制教学视频的工具。 一、PPT录屏 在windows系统中有个常用软件就是ppt,ppt也能够录屏,只是我们大家都忽…

postgresql数据库流复制

primary和standby在同一服务器的不同目录下。 primary: /var/postgre/data standby: /var/postgre/data1 1. 加账号 (也可以使用postgres账号) [postgresv-vlxsz-devdb01 data]$ psql psql (14.1) Type "help" for help. postgres# create role replica with r…

自动化制药设备中PLC如何进行远程监控?有何应用场景

随着科学技术水平的不断提高,制药设备也逐步走向自动化。自动化制药设备实现到精确的位置定位,实时的过程监控,快速的网络通讯,在到现场的每个动作的执行,具备高效率的生产特点。PLC作为现在工业设备控制系统的核心&am…

php-fpm 的状态查看和php-fpm 调优随笔

php-fpm status 配置 1. 找到php-fpm的配置文件,将 pm.status_path /status 注释去掉 重启php-fpm 2. 在nginx 中配置一个server块或者配置一个location 3. 最后通过curl 或者 http 请求可以看到php-fpm 的状态 可以带着参数访问,json、xml、html fu…

Android -- 每日一问:在项目中使用AsyncTask会有什么问题吗?

经典回答 那我们考查 AsyncTask 会问些什么呢?得先问问会不会用吧,看看知不知道有 onProgressUpdate 方法。 其次问一下是怎么理解 AsyncTask 的机制,有没有看过它的源代码? 这个问题主要看对方是否对 Android 的东西有好奇心&am…

夜天之书 #69 企业开源该选什么软件许可证?

开源软件和自由软件的概念与其许可证紧密绑定。通常,开源软件被定义为使用 OSI 认可的,即符合开源定义[1]的许可证来分发的软件,而自由软件被定义成使用 GPL 或说 Copyleft 式许可证分发的软件。尽管今天人们最关心的可能是软件的生产过程即如…