解析c++空指针解引用奔溃
空指针解引用引起程序奔溃是c/c++中最常见的稳定性错误之一。
显然并非所有使用空指针的语句都会导致奔溃,那什么情况下使用空指针才会引起程序奔溃呢?有一个判断标准:判断空指针是否会导致访问非法内存的情况,如果会导致访问非法内存就会奔溃,否则不会奔溃。
常见的空指针操作
考虑下面的代码,用到空指针test
的6条语句(#1
~#6
)中哪些会引起程序奔溃?
struct Test {void method_01() { }virtual void method_02() { };int value;static void StFunction() { }static int stValue;
};
int Test::stValue = 1;int main() {Test* test = nullptr;Test copy = *test; // #1int value = test->value; // #2 int stVAlue = test->stValue; // #3test->StFunction(); // #4test->method_01(); // #5test->method_02(); // #6
}
答案如下
序号 | 代码含义 | 是否会引起程序奔溃 |
---|---|---|
#1 | 对指针取值 | 是 |
#2 | 通过指针访问成员变量 | 是 |
#3 | 通过指针访问静态变量 | 否 |
#4 | 通过指针调用静态函数 | 否 |
#5 | 通过指针调用成员函数 | 否 |
#6 | 通过指针调用虚函数 | 是 |
面对这个答案大家可能会有疑问:
- 为什么空指针
test
访问成员变量会奔溃而访问静态变量不会奔溃? - 为什么空指针
test
调用静态函数和非虚成员函数不会奔溃而调用虚函数会奔溃?
原因隐藏在“对空指针解引用会引发程序奔溃”这句话的关键词解引用里。怎么理解引用呢?可以简单理解为访问与指针有关的内存地址。
从程序运行的角度来看,问题的本质是访问非法内存会引起程序奔溃。所以空指针是否会引起程序奔溃的一个判断标准总结为:判断空指针是否会导致访问非法内存的情况,如果会导致访问非法内存就会奔溃,否则不会奔溃。
接下来我们逐个分析#1
~#6
这些语句的内存访问情况,会涉及到一些c++底层知识,也是本文的主要内容。
深入理解
在详细分析之前先看看Test
类的内存结构
注意:虚函数表和虚函数表指针不是必须的,只有定义或者继承了虚函数的类型才会分配这两块内存。
内存分为两部分(可以结合进程的内存结构和ELF文件结构来理解):
● 静态内存:编译阶段确定地址的内存,与实例无关且全局只存在一份。如静态变量、虚函数表、代码段。
● 动态内存:运行阶段才能确定地址的内存,与实例绑定。如成员变量、虚函数表指针(虚表指针实际上也是成员变量,特殊在它是由编译器添加的)。
所以用到空指针test
的6条语句本身访问内存情况如下
编号 | 操作 | 访问符号 | 符号类型 | 符号地址 | 备注 |
---|---|---|---|---|---|
Test* test = nullptr; | - | - | - | - | |
#1 | Test copy = *test; | test | 指针类型局部变量 | - | - |
#2 | int value = test->value; | Test::value | 成员变量 | 0x8 | value 相对Test 首地址的偏移量为8字节,因此地址为 0x0 + 8 = 0x8 |
#3 | int stVAlue = test->stValue; | Test::stValue | 静态变量 | 固定地址 | 编译阶段分配好的地址,与指针test 无关 |
#4 | test->StFunction(); | Test::StFunction | 静态函数 | 固定地址 | 编译阶段分配好的地址,与指针test 无关 |
#5 | test->method_01(); | Test::method_01 | 非虚成员函数 | 固定地址 | 编译阶段分配好的地址,与指针test 无关 |
#6 | test->method_02(); | 虚函数表指针 | 指针类型成员变量 | 0x0 | 虚函数表指针 相对Test 首地址的偏移量为0字节,因此地址为 0x0 + 0 = 0x0 |
↑ | ↑ | Test::method_02 | 虚函数 | 固定地址 | 编译阶段分配好的地址,与指针test 无关 |
了解Test
的内存结构之后,分析空指针test
的6条语句是否会引起程序奔溃就变得清晰很多:
#1
取值操作:Test copy = *test;
空指针test
指向的地址是0x0,取值操作*test
访问的是非法内存地址0x0,所以会引起程序奔溃。
#2
访问成员变量:int value = test->value;
test->value
是在访问非法地址0x8,所以会引起程序奔溃。
#3
访问静态变量:int stVAlue = test->stValue;
访问静态变量和静态函数的方式有2种
● 通过实例访问:例如int stVAlue = test->stValue;
、test->StFunction();
● 通过类名访问:例如int stVAlue = Test::stValue;
、Test::StFunction();
两种访问方式的效果是一样的,实际上通过类名访问的方式更常见。本文使用通过实例访问的方式做示例是为了与其他操作做对比。将示例代码访中问静态变量和静态函数的语句替换成通过类名访问的方式后,会发现访问静态变量和调用静态函数的语句与
test
指针本身没有半毛钱关系。
test->stValue
等价于Test::stValue
,这条语句访问的是stValue
的地址而这个地址必然是有效的,与空指针test
没有任何关系,所以不会引起程序奔溃。
#5
调用非虚成员函数:test->method_01();
成员函数的本质
从内存结构上看成员函数和静态函数似乎没有区别,实际上他俩确实没有区别。可以这样理解:c++是比c语言多了很多特性的增强版,成员函数就是其中一个特性,这个特性类似于语法糖,目的是为了简化调用成员函数(一种特殊的函数)的语法。成员函数特殊在第一个形参一定是this指针(隐式形参,不需要明确定义,编译器会在编译阶段补全),所以我们可以把成员函数退化成等价的c风格全局函数,例如
● 定义退化:成员函数void Test::method_01()
可以退化成全局函数void method_01(Test* self)
● 调用退化:调用成员函数test->method()
可以退化成调用全局函数method_01(test)
同样,test->method_01
相当于method_01(test)
,是在访问method_01
的地址而这个地址必然是有效的,虽然入参test
是空指针,但调用函数这条语句本身不会访问这个空指针的内存,因此不会引起程序奔溃。
注意:调用非虚成员函数这条语句本身不会引起奔溃,但由于通常情况下成员函数的实现都会访问成员变量,所以程序可能会在成员函数内部因为解引用空指针this
(也就是入参test
)而奔溃。最常见具有迷惑性的奔溃现场比如
● 构造函数的内部空指针错误 —— 在访问成员变量或者虚函数的语句奔溃;
● 非虚析构函数内部空指针错误 —— 在访问成员变量或者虚函数的语句奔溃;
构造函数和非虚析构函数是特殊的非虚成员函数,在分析奔溃问题的时候可以把他们当作普通的非虚成员函数一样对待。
#6
调用虚函数:test->method_02();
虚函数调用过程
虚函数是c++多态的核心技术(不知道多态是什么的同学出门右转找个角落自己学习一下),保证在继承结构中能正确调用子类的实现。虚函数表、虚函数表指针就是用来完成虚函数调用的,调用虚函数主要有下面几个步骤:
● 通过虚函数指针访问对应的虚函数表;例如Test
的实例的虚函数指针指向Test
的虚函数表;
● 在虚函数表中找到需要调用的函数;
● 调用这个函数;
调用虚函数的情况与调用非虚函数有所不同,test->method_02()
不会直接访问函数method_02()
的地址,而是首先通过虚函数表指针访问虚函数表,在通过空指针test
访问虚函数指针时会访问非法地址0x0,因此会引起程序奔溃。
相关文章:
解析c++空指针解引用奔溃
空指针解引用引起程序奔溃是c/c中最常见的稳定性错误之一。 显然并非所有使用空指针的语句都会导致奔溃,那什么情况下使用空指针才会引起程序奔溃呢?有一个判断标准:判断空指针是否会导致访问非法内存的情况,如果会导致访问非法内…...
Oracle START WITH 递归语句的使用方法及示例
Oracle数据库中的START WITH语句经常与CONNECT BY子句一起使用,以实现对层次型数据的查询。这种查询模式非常适用于处理具有父子关系的数据,如组织结构、分类信息等。 理解START WITH和CONNECT BY 在层次型查询中,START WITH定义了层次结构…...
使用Windbg动态调试目标进程的一般步骤详解
目录 1、概述 2、将Windbg附加到已经启动起来的目标进程上,或者用Windbg启动目标程序 2.1、将Windbg附加到已经启动起来的目标进程上 2.2、用Windbg启动目标程序 2.3、Windbg关联到目标进程上会中断下来,输入g命令将该中断跳过去 3、分析实例说明 …...
Linux驱动学习—输入子系统
1、什么是输入子系统? 输入子系统是Linux专门做的一套框架来处理输入事件的,像鼠标,键盘,触摸屏这些都是输入设备,但是这邪恶输入设备的类型又都不是一样的,所以为了统一这些输入设备驱动标准应运而生的。…...
计算机网络(2)
计算机网络(2) 小程一言专栏链接: [link](http://t.csdnimg.cn/ZUTXU) 计算机网络和因特网(2)分组交换网中的时延、丢包和吞吐量时延丢包吞吐量总结 协议层次及其服务模型模型类型OSI模型分析TCP/IP模型分析 追溯历史 小程一言 我…...
什么是预训练Pre-training—— AIGC必备知识点,您get了吗?
Look!👀我们的大模型商业化落地产品📖更多AI资讯请👉🏾关注Free三天集训营助教在线为您火热答疑👩🏼🏫 随着人工智能(AI)不断重塑我们的世界,其发展的一个关键方面已经…...
bat脚本sqlserver 不同数据库同步
如果你想使用批处理脚本(.bat)在 SQL Server 中同步不同数据库的数据,你可以考虑以下步骤: 设置环境变量: 确保你的系统环境变量中已经设置了 SQLCMD 和 BCP 的路径。 编写批处理脚本: 使用 sqlcmd 来执行…...
阶段十-分布式-Redis02
第一章 Redis 事务 1.1 节 数据库事务复习 数据库事务的四大特性 A:Atomic ,原子性,将所以SQL作为原子工作单元执行,要么全部执行,要么全部不执行;C:Consistent,一致性࿰…...
微信小程序实战-02翻页时钟-2
微信小程序实战系列 《微信小程序实战-01翻页时钟-1》 文章目录 微信小程序实战系列前言计时功能实现clock.wxmlclock.wxssclock.js 运行效果总结 前言 接着《微信小程序实战-01翻页时钟-1》,继续完成“6个页面的静态渲染和计时”功能。 计时功能实现 clock.wxm…...
每天刷两道题——第十一天
1.1滑动窗口最大值 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值 。 输入:nums [1,3,-1,-3,5,3,6,7], k 3 输出&…...
Git提交规范
一. 修改类型 每个类型值都表示了不同的含义,类型值必须是以下的其中一个: feat:提交新功能fix:修复了bugdocs:只修改了文档style:调整代码格式,未修改代码逻辑(比如修改空格、格式…...
apache2的虚拟主机的配置
APACHE2的虚拟主机配置 本章中心概括: 虚拟web主机的初步认识,在redhat系列系统中如何配置,在Debian系列系统中如何配置。 什么是apache2虚拟主机: 简单点讲,就是在同一个物理机中配置多个虚拟主机,从而达…...
Provide/Inject 依赖注入(未完待续)
父组件传递给子组件数据,通过props,但是需要逐层传递 provide/Inject 的推出就是为了解决这个问题,它提供了一种组件之间共享此类值的方式,不必通过组件树每层级显示地传递props 目的是为了共享那些被 认为对于一个组件树而言是全局的数据 p…...
力扣173. 二叉搜索树迭代器
深度优先搜索 思路: 遍历二叉搜索树,左子树总比根节点小,右子树总比根节点大;先深度遍历左子树,然后返回其父节点,然后遍历其右子树节点;使用栈数据结构存储节点数据,借用其“后进先…...
电脑找不到d3dcompiler43.dll怎么修复,教你5个可靠的方法
d3dcompiler43.dll是Windows操作系统中的一个重要动态链接库文件,主要负责Direct3D编译器的相关功能。如果“d3dcompiler43.dll丢失”通常会导致游戏无法正常运行或者程序崩溃。为了解决这个问题,我整理了以下五个解决方法,希望能帮助到遇到相…...
5.3 Android BCC环境搭建(eadb版 上)
写在前面 eadb即eBPF Android Debug Bridge,它是基于adeb的重构。后者曾随aosp 10发布在platform/external目录下。 一,root权限 这里再HighLight下,当前整个专栏都是基于开发环境来展开的,也就是Android设备需要具有root权限。因此该专栏下每一篇博客都是默认了当前开发…...
【算法题】44. 通配符匹配
题目 给你一个输入字符串 (s) 和一个字符模式 (p) ,请你实现一个支持 ? 和 * 匹配规则的通配符匹配: ? 可以匹配任何单个字符。 * 可以匹配任意字符序列(包括空字符序列)。 判定匹配成功的充要条件是:字符模式必须能…...
vscode配置与注意事项
中文设置 https://zhuanlan.zhihu.com/p/263036716 应用搜索输入“Chinese (Simplified) Language Pack for Visual Studio Code”并敲回车键 底部信息窗没有的话 首先使用快捷键ctrlshiftp,Mac用户使shiftcommandp,然后输入settings.json 将下面的选…...
设计模式篇章(3)——七种结构型模式
结构型设计模式主要思考的是如何将对象进行合理的布局来组成一个更大的功能体或者结构体,这个现在讲有点抽象,用大白话讲就是利用现有的对象进行组合或者配合,使得组合后的这个系统更加好。好是相对于不使用设计模式,按照自己的堆…...
Window端口占用处理
您好,我是码农飞哥(wei158556),感谢您阅读本文,欢迎一键三连哦。 💪🏻 1. Python基础专栏,基础知识一网打尽,9.9元买不了吃亏,买不了上当。 Python从入门到精…...
算法实战(二)
基础算法编程 题目来源([PAT题目](https://pintia.cn/problem-sets/14/exam/problems/type/6))7-2 然后是几点7-3 逆序的三位数7-6 混合类型数据格式化输入 题目来源(PAT题目) 7-2 然后是几点 有时候人们用四位数字表示一个时间,比如 1106 表示 11 点零 6 分。现在…...
网工内推 | 上市公司网工,NP认证优先,最高15薪+项目奖金
01 广东轩辕网络科技股份有限公司 招聘岗位:网络工程师 职责描述: 1、主要负责教育行业园区网的有线及无线网络项目的实施、维护、巡检等工作; 2、协助windows/linux平台服务器OS的安装、部署、配置与维护; 3、协助服务器、存储、…...
【LLM 论文阅读】NEFTU N E: LLM微调的免费午餐
指令微调的局限性 指令微调对于训练llm的能力至关重要,而模型的有用性在很大程度上取决于我们从小指令数据集中获得最大信息的能力。在本文中,我们提出在微调正向传递的过程中,在训练数据的嵌入向量中添加随机噪声,论文实验显示这…...
JS新手入门笔记整理:对象
对象可以分为两种:一种是“自定义对象”,另外一种是“内置对象”。自定义对象,指的是需要我们自己定义的对象。内置对象,指的是不需要我们自己定义的(即系统已经定义好的)、可以直接使用的对象。在JavaScri…...
Python GIL 一文全知道!
GIL 作为 Python 开发者心中永远的痛,在最近即将到来的更新中,终于要彻底解决了,整个 Python 社群都沸腾了 什么是GIL? GIL是英文学名global interpreter lock的缩写,中文翻译成全局解释器锁。GIL需要解决的是线程竞…...
数据库级别的MD5加密(扩展)
首先,我们要知道什么是MD5? 1.主要是增强算法的复杂性和不可逆性 2.MD5不可逆,具体的值MD5是一样的 3.MD5破解网站的原理,背后有一个字典 代码案例: -- 加密 update testMD5 set pwdmd5(pwd) where id1; update testMD5 set…...
Docker安装Jenkins,配置Maven和Java
前言 这是一个java的springboot项目,使用maven构建 安装准备 需要将maven和jdk安装在服务器上,Jenkins需要用到,还有创建一个jenkins的目录,安装命令如下: docker run -d -uroot -p 9095:8080 -p 50000:50000 --n…...
游戏分组(100用例)C卷 (JavaPythonC语言C++Node.js)
部门准备举办一场王者荣耀表演赛,有10名游戏爱好者参与,分为两队,每队5人。 每位参与者都有一个评分,代表着他的游戏水平。为了表演赛尽可能精彩,我们需要把10名参赛者分为实力尽量相近的两队。一队的实力可以表示为这一队5名队员的评分总和。 现在给你10名参与者的游戏水…...
python函数装饰器保存信息
1 python函数装饰器保存信息 python函数装饰器,可以通过实例属性、全局变量、非局部变量和函数属性,来保存被装饰函数的状态信息。 1.1 统计调用并跟踪 描述 通过装饰器统计函数调用次数,并且用打印来跟踪调用记录。 此装饰器用类的__ca…...
AI真正的Killer App 仍然缺席
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
wordpress淘宝客模板修改教程/谷歌广告优化
SQL分类 DDL(Data Definition Languages) 数据定义语言,这些语句定义了不同的数据字段、数据库、表、列、索引等数据。 常用的语句关键字主要包括 create(添加),drop(删除),alter(修…...
做百科需要用什么网站做参考/种子库
微友提问您好,凌老师!有没有专门针对教学中PPT课件制作的书推荐或是网课,如果有光盘更好。因为每次做的课件都要搜索好多网页才弄出一个效果,想暑假好好学习一下。下面是我看到过印象比较深刻的一些效果,不知道是如何实…...
小型网站建设价格低/新闻网站软文平台
今天介绍一款特别好用的流程图、思维导图软件ProcessOn 用途:在线画流程图、思维导图、UI原型图、UML、网络拓扑图、组织结构图等 各种模板供你选择 支持团队协作支持不同格式下载 更多查看官网最后附上做的效果图: 转载于:https://juejin.im/post/5bf39…...
网站图片上的水印怎么做/免费模板
Win11 安装 WSA(安卓子系统)教程 参考:http://www.xitongzhijia.net/xtjc/20211008/228813.html 上手需要一些 “命令行” 基础,大概了解 WSL WSA Hyper-V 等概念 微软尚未正式推送 WSA,之前看了下需要测试通道就没…...
无法访问香港网站/seo内部优化方案
MSXML2能够很好地处理xml文件,不过也有些需要注意的点1. MSXML2的加载和释放 loadIndexXml(TCHAR * indexdir) {if (NULL indexdir){return NULL;}::CoInitialize(NULL);MSXML2::IXMLDOMDocumentPtr doc;doc.CreateInstance(__uuidof(DOMDocument60));if (VARIANT_…...
班级网站建设需求分析/东莞seo优化方案
共享内存 共享内存为多个进程之间共享和传递数据提供了一种有效的方式。共享内存是先在物理内存上申请一块空间,多个进程可以将其映射到自己的虚拟地址空间中。所有进程都可以访问共享内存中的地址,就好像它们是由 malloc 分配的一样。如果某个进程向共…...