解析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从入门到精…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)
安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...
【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...
