C 语言中的 volatile 关键字
1、概念
volatile 是 C/C++ 语言中的一个类型修饰符,用于告知编译器:该变量的值可能会在程序控制流之外被意外修改(如硬件寄存器、多线程共享变量或信号处理函数等),因此编译器不应对其进行激进的优化(如缓存到寄存器或消除冗余读取)。
在程序运行时,编译器通常会假设变量的值仅由当前线程或函数内的代码修改,并据此进行优化(如循环内变量提升、指令重排等)。然而,在嵌入式开发、设备驱动编程或多线程环境中,某些变量的值可能被外部因素(如硬件中断、信号处理器、其他线程)异步修改。此时,若未使用 volatile 修饰,编译器可能生成错误的优化代码,导致程序行为异常。
简而言之,volatile 的作用是:
- 阻止编译器优化:强制每次访问变量时都从内存读取,而非使用寄存器中的缓存值;
- 确保内存可见性:防止编译器重排或省略对变量的访问,保证操作顺序符合预期;
- 适用于特殊场景:如硬件寄存器映射、信号处理、多线程共享变量(需配合其他同步机制)
volatile 并不解决所有并发问题(如原子性),但它是底层编程中确保正确内存访问的重要工具。
2、代码测试
下面是在 ARM 平台的 C 语言测试,因为 ARM 是弱内存模型,更容易复现问题。
/** volatile_test.c*/#include <stdio.h>
#include <signal.h>
#include <unistd.h>// 全局变量,使用或不使用volatile修饰
int flag = 0; // 尝试改为 volatile int flag = 0; 观察不同结果void handler(int sig) {flag = 1;printf("Signal handler set flag to 1\n");
}int main() {signal(SIGALRM, handler);alarm(1); // 1秒后发送SIGALRM信号while(!flag) {// 空循环等待flag变化}printf("Main loop detected flag change\n");return 0;
}
2.1 测试结果
不使用 volatile 关键字,程序会卡死在 while 循环中:
liang@liang-virtual-machine:~/cfp$../arm-none-linux-gnueabi-gcc -O3 -g -o volatile_test volatile_test.c
liang@liang-virtual-machine:~/cfp$ ./volatile_test
Signal handler set flag to 1
使用 volatile,程序正常退出
liang@liang-virtual-machine:~/cfp$../arm-none-linux-gnueabi-gcc -O3 -g -o volatile_test volatile_test.c
liang@liang-virtual-machine:~/cfp$ ./volatile_test
Signal handler set flag to 1
Main loop detected flag change
liang@liang-virtual-machine:~/cfp$
2.2 反汇编
不使用 volatile:
liang@liang-virtual-machine:~/cfp$../arm-none-linux-gnueabi-objdump -S volatile_test
......
int main() {83cc: e92d4010 push {r4, lr}signal(SIGALRM, handler);83d0: e59f1030 ldr r1, [pc, #48] ; 8408 <main+0x3c>83d4: e3a0000e mov r0, #14 ; 0xe83d8: ebffffc5 bl 82f4 <_init+0x38>alarm(1); // 1秒后发送SIGALRM信号83dc: e3a00001 mov r0, #1 ; 0x183e0: ebffffc9 bl 830c <_init+0x50>83e4: e59f3020 ldr r3, [pc, #32] ; 840c <main+0x40>83e8: e5932000 ldr r2, [r3] ; 从内存读取 flag 值到 r283ec: e3520000 cmp r2, #0 ; 0x0 ; 比较 r2 的值83f0: 1a000000 bne 83f8 <main+0x2c> ; 如果 r2≠0,跳转到 83f8 位置83f4: eafffffe b 83f4 <main+0x28> ; 无条件跳转到自身(无限循环)while(!flag) {// 空循环等待flag变化}printf("Main loop detected flag change\n");83f8: e59f0010 ldr r0, [pc, #16] ; 8410 <main+0x44>83fc: ebffffc5 bl 8318 <_init+0x5c>return 0;
}
......
可以看到,编译器对 while 循环做了优化。编译器只在循环开始前读取一次 flag 的值到寄存器 r2。编译器认为 flag 在循环内不会被修改,之后循环中不再重新从内存读取 flag。同时,直接做了一个无条件跳转到自身的优化:
83f4: eafffffe b 83f4 <main+0x28> ; 无条件跳转到自身(无限循环)
而对比使用 volatile 关键字,可以看到编译器没有对 while 循环做优化,每次循环都重新读取 flag 的值:
liang@liang-virtual-machine:~/cfp$../arm-none-linux-gnueabi-objdump -S volatile_test
......
int main() {83cc: e92d4010 push {r4, lr}signal(SIGALRM, handler);83d0: e59f102c ldr r1, [pc, #44] ; 8404 <main+0x38>83d4: e3a0000e mov r0, #14 ; 0xe83d8: ebffffc5 bl 82f4 <_init+0x38>alarm(1); // 1秒后发送SIGALRM信号83dc: e3a00001 mov r0, #1 ; 0x183e0: ebffffc9 bl 830c <_init+0x50>83e4: e59f201c ldr r2, [pc, #28] ; 8408 <main+0x3c>while(!flag) {83e8: e5923000 ldr r3, [r2] ; 每次循环都重新读取flag83ec: e3530000 cmp r3, #0 ; 0x0 ; 如果≠0跳转到退出83f0: 0afffffc beq 83e8 <main+0x1c> ; 继续循环// 空循环等待flag变化}printf("Main loop detected flag change\n");83f4: e59f0010 ldr r0, [pc, #16] ; 840c <main+0x40>83f8: ebffffc6 bl 8318 <_init+0x5c>return 0;
}
......
相关文章:
C 语言中的 volatile 关键字
1、概念 volatile 是 C/C 语言中的一个类型修饰符,用于告知编译器:该变量的值可能会在程序控制流之外被意外修改(如硬件寄存器、多线程共享变量或信号处理函数等),因此编译器不应对其进行激进的优化(如缓存…...
mysql表类型查询
普通表 SELECT table_schema AS database_name,table_name FROM information_schema.tables WHERE table_schema NOT IN (information_schema, mysql, performance_schema, sys)AND table_type BASE TABLEAND table_name NOT IN (SELECT DISTINCT table_name FROM informatio…...
JavaScript事件循环
目录 JavaScript 执行机制与事件循环 一、同步与异步代码 1. 同步代码(Synchronous Code) 2. 异步代码(Asynchronous Code) 二、事件循环(Event Loop) 1. 核心组成 2. 事件循环基本流程 3. 运行机制…...
Linux》》bash 、sh 执行脚本
通常使用shell去运行脚本,两种方法 》bash xxx.sh 或 bash “xxx.sh” 、sh xxx.sh 或 sh “xxx.sh” 》bash -c “cmd string” 引号不能省略 我们知道 -c 的意思是 command,所以 bash -c 或 sh -c 后面应该跟一个 command。...
Git完全指南:从入门到精通版本控制 ------- Git 查看提交历史(8)
Git提交历史深度解析:从代码考古到精准回退 前言 在软件开发的生命周期中,提交历史是团队协作的时空胶囊。Git作为分布式版本控制系统,其强大的历史追溯能力可帮助开发者: 精准定位引入Bug的提交分析代码演进趋势恢复误删的重要…...
精益数据分析(2/126):解锁数据驱动的商业成功密码
精益数据分析(2/126):解锁数据驱动的商业成功密码 大家好!在如今这个数据爆炸的时代,数据就像一座蕴含无限宝藏的矿山,等待着我们去挖掘和利用。最近我在深入研读《精益数据分析》这本书,收获了…...
【ssti模板注入基础】
一、ssti模板注入简介 二、模板在开发中的应用 为什么要使用模板 为什么要用模板来提升效率: 不管我们输入什么,有一部分内容都是不会变的 除了内容之外其他都不会变,如果我们有成千上万的页面,如果不用模板,就算复…...
如何在 Kali 上解决使用 evil-winrm 时 Ruby Reline 的 quoting_detection_proc 警告
在使用 Kali Linux 运行 Ruby 工具(例如 evil-winrm)时,你可能会遇到以下警告: Warning: Remote path completions is disabled due to ruby limitation: undefined method quoting_detection_proc for module Reline这个警告会导…...
从零开始搭建PyTorch环境(支持CUDA)
从零开始搭建PyTorch环境(支持CUDA) 本文将详细介绍如何在Windows系统上为RTX 3050显卡配置支持CUDA的PyTorch环境。 环境准备 本教程基于以下环境: 显卡:NVIDIA RTX 3050操作系统:WindowsPython版本:3.1…...
【扩散模型连载 · 第 2 期】逆向扩散建模与神经网络的角色
上期回顾 我们在第 1 期中介绍了 正向扩散过程(Forward Process),并用 CIFAR-10 图像演示了加噪过程: 正向过程是固定的,无需训练,但我们感兴趣的是:如何从纯噪声一步步“还原”出真实图像&…...
Mysql约束
约束其实就是创建表的时候给表的某些列加上限制条件。 主键约束和自增长约束比较重要 一、Mysql约束-主键约束 简介 指定的主键不能重复也不可以出现空值 1.添加单列主键 语法1:create table 表名(字段名 数据类型 primary key); 点开…...
力扣热题100—滑动窗口(c++)
3.无重复字符的最长子串 给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。 unordered_set<char> charSet; // 用于保存当前窗口的字符int left 0; // 窗口左指针int maxLength 0; // 最长子串的长度for (int right 0; right < s.siz…...
Linux网络编程第一课:深入浅出TCP/IP协议簇与网络寻址系统
知识点1【网络发展简史】 **网络节点:**路由器和交换机组成 交换机的作用:拓展网络接口 路由:网络通信路径 1、分组交换 分组的目的: 数据量大,不能一次型传输,只能分批次传输,这里的每一批…...
论文阅读笔记:Generative Modeling by Estimating Gradients of the Data Distribution
1、参考来源 论文《Generative Modeling by Estimating Gradients of the Data Distribution》 来源:NeurIPS 2019 论文链接:https://arxiv.org/abs/1907.05600 参考链接: 【AI知识分享】真正搞懂扩散模型Score Matching一定要理解的三大核心…...
C++零基础实践教程 函数 数组、字符串与 Vector
模块四:函数 (代码复用与模块化) 随着程序变得越来越复杂,把所有代码都堆在 main 函数里会变得难以管理和阅读。函数 (Function) 允许你将代码分解成逻辑上独立、可重用的块。这就像把一个大任务分解成几个小任务,每个小任务交给一个专门的“…...
照片处理工具:基于HTML与JavaScript实现详解
在当今数字时代,处理照片已成为日常需求。 本文将详细介绍一个基于HTML和JavaScript的照片处理工具的实现原理,这个工具可以调整图片尺寸、格式,并精确控制输出文件大小。 实现如下,不需要任何编辑器,txt文本、浏览器就行!! 工具功能概述 这个照片处理工具提供以下核心…...
MyBatis-OGNL表达式
介绍 OGNL(Object-Graph Navigation Language)是一种强大的表达式语言,用于获取和设置Java对象图中的属性。在MyBatis中,OGNL常用于动态SQL构建,如条件判断、循环等。以下是关于OGNL表达式的整合信息,包括…...
Web Worker在uniapp鸿蒙APP中的深度应用
文章目录 一、Web Worker核心概念解析1.1 什么是Web Worker?1.2 为什么在鸿蒙APP中使用Web Worker?1.3 性能对比实测 二、uniapp中的Web Worker完整实现2.1 基础配置步骤2.1.1 项目结构准备2.1.2 鸿蒙平台特殊配置 2.2 Worker脚本编写规范2.3 主线程通信…...
无人机故障冗余设计技术要点与难点!
一、技术要点 1. 冗余架构设计 硬件冗余:关键部件(飞控、电机、电池、通信模块)采用双余度或三余度设计,例如: 双飞控系统:主飞控失效时,备用飞控无缝接管。 电机动力冗余:六轴无…...
MySQL数据库表查询
测试表company.employee5 mysql> create database company; #创建一个库; 创建一个测试表: mysql> CREATE TABLE company.employee5(id int primary key auto_increment not null,name varchar(30) not null,sex enum(male,female) default male not null,hi…...
ADB的安装及抓取日志(2)
三、ADB抓取日志 在使用ADB抓取日志前,首先要保证电脑已经安装并配置ADB,在上一节已经验证完成。连接设备:可通过USB或者WI-FI,将安卓设备与电脑连接,并启用USB调试模式,此处我选择的是通过电脑与安卓设备…...
【C++】 —— 笔试刷题day_17
一、小乐乐改数字 题目解析 这道题,它们给定一个数,我们要对它进行修改;如果某一位是奇数,就把它变成1,;如果是偶数,就把它变成0; 让我们输出最后得到的数。 算法思路 这道题,总体…...
traceId传递
1、应用内传递通过ThreadLocal,InheritableThreadLocal传递 2、跨进程的应用间传递,这种会涉及到远程rpc通信,mq通信,数据库通信等。 feign:拦截器中改变请求头 feign.RequestInterceptor, 这个机制能够实现修改请求对象的目的,…...
自然科技部分详解
光的反射 凸面镜与凹面镜 凸透镜和凹透镜 空气开关原理 短路是指电路中突然的电流过大,这会让线圈的磁性增大,来克服内设的弹簧导致断开 过载会让电流增大,两金属片受热膨胀触发断开 核电荷数是指原子核所带的电荷数。 在原子中…...
蓝桥杯 9. 九宫幻方
九宫幻方 原题目链接 题目描述 小明最近在教邻居家的小朋友小学奥数,而最近正好讲述到了三阶幻方这个部分。 三阶幻方是指将 1 ~ 9 不重复地填入一个 33 的矩阵中,使得每一行、每一列和每一条对角线的和都是相同的。 三阶幻方又被称作九宫格&#x…...
算法——希尔排序
目录 一、希尔排序定义 二、希尔排序原理 三、希尔排序特点 四、两种解法 五、代码实现 一、希尔排序定义 希尔排序是一种基于插入排序的排序算法,也被称为缩小增量排序。它通过将待排序的数组分割成若干个子序列,对子序列进行排序,然后…...
亚马逊热销变维权?5步搭建跨境产品的安全防火墙
“产品热卖,引来维权”——这已经悄然成为越来越多跨境卖家的“热销烦恼”。曾经拼品拼量,如今却要步步谨慎。商标侵权、专利投诉、图片盗用……这些问题一旦发生,轻则下架、账号被限,重则冻结资金甚至封店。 别让“热销”变“受…...
20250416-Python 中常见的填充 `pad` 方法
Python 中常见的填充 pad 方法 在 Python 中,pad 方法通常与字符串或数组操作相关,用于在数据的前后填充特定的值,以达到指定的长度或格式。以下是几种常见的与 pad 相关的用法: 1. 字符串的 pad 操作 虽然 Python 的字符串没有…...
JavaEE-0416
今天修复了一个查询数据时数据显示哈希码: 搜索检阅后得到显示该格式的原因: 重写 POJO 类的 toString 方法 在 Java 编程中,默认情况下,对象的 toString() 方法会返回类似于 com.cz.pojo.Score2a266d09 的字符串。这是由于默认…...
团体程序设计天梯赛L2-008 最长对称子串
对给定的字符串,本题要求你输出最长对称子串的长度。例如,给定Is PAT&TAP symmetric?,最长对称子串为s PAT&TAP s,于是你应该输出11。 输入格式: 输入在一行中给出长度不超过1000的非空字符串。 输出格式&…...
命令模式 (Command Pattern)
命令模式(Command Pattern)是一种行为型设计模式,它将请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。该模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。 一、基础 1.1 意图 将请求封…...
Elasticsearch 8.18 中提供了原生连接 (Native Joins)
作者:来自 Elastic Costin Leau 探索 LOOKUP JOIN,这是一条在 Elasticsearch 8.18 的技术预览中提供的新 ES|QL 命令。 很高兴宣布 LOOKUP JOIN —— 这是一条在 Elasticsearch 8.18 的技术预览中提供的新 ES|QL 命令,旨在执行左 joins 以进行…...
在线终端(一个基于 Spring Boot 的在线终端模拟器,实现了类 Linux 命令行操作功能)
Online Terminal 一个基于 Spring Boot 的在线终端模拟器,实现了类 Linux 命令行操作功能。 功能特点 模拟 Linux 文件系统操作支持基础的文件和目录管理命令提供文件内容查看和编辑功能支持文件压缩和解压缩操作 快速开始 环境要求 JDK 8Maven 3.6 运行项目 克隆项目到…...
vue+electron ipc+sql相关开发(三)
在 Electron 中使用 IPC(Inter-Process Communication)与 SQLite 数据库进行通信是一个常见的模式,特别是在需要将数据库操作从渲染进程(Vue.js)移到主进程(Electron)的情况下。这样可以更好地管理数据库连接和提高安全性。下一篇介绍结合axios写成通用接口形式,虽然没…...
C++静态变量多线程中的未定义行为
静态变量,是 C 程序员最早接触的语言特性之一。它有状态、生命周期长、初始化一次,用起来真是香。 但只要程序一旦进入多线程的世界,很多你原以为“稳定可靠”的写法,可能就突然开始“不对劲”了。静态变量首当其冲。 今天我们就…...
黑马商城项目(二) Docker
一、Docker快速入门 安装Docker - 飞书云文档 二、命令解读 常见命令: 数据卷: 案例1 数据卷挂载: 案例2 本地目录挂载: 挂载到指定目录能够保存数据(即使Mysql容器被删除) docker run -d \--name mysql …...
玩转Docker | 使用Docker部署Memos笔记工具
玩转Docker | 使用Docker部署Memos笔记工具 前言一、Memos介绍Memos简介主要特点二、系统要求环境要求环境检查Docker版本检查检查操作系统版本三、部署Memos服务下载镜像创建容器创建容器检查容器状态检查服务端口安全设置四、访问Memos服务访问Memos首页注册账号五、基本使用…...
c#从ftp服务器下载文件读取csv
从 FTP 服务器下载文件的功能,并且支持根据文件名称的前缀或直接文件名进行查找和下载。以下是对代码的一些建议和修改,以确保它能够满足您的需求,尤其是如果您希望仅下载特定类型的文件(例如 .csv 文件) using Syste…...
电脑知识 | TCP通俗易懂详解 <三>tcp首部中ACK、SYN、FIN等信息填写案例_握手时
目录 一、👋🏻前言 二、🤝🏻握手时的快递单 1.👫第一次握手(发送方) 2.👫第二次握手(收件方) 3.👫第三次握手(发件方)…...
go学习记录(第二天)
Java里面的类对象可以对应go里面的结构体吗 表格对比 Java 类 (Class)Go 结构体 (Struct)封装数据和行为(字段方法)主要封装数据(字段),方法通过接收者关联支持继承(extends…...
Docker 中启动 Nginx 容器
文章目录 1. 快速运行 Nginx 容器从 Docker Hub 拉取官方镜像并运行:验证访问: 2. 挂载自定义配置和静态文件步骤: 3. 常用操作命令4. 生产环境建议使用 Docker Compose关键优化: 5. 调试技巧6. 常见问题解决 1. 快速运行 Nginx 容…...
windows 11 安装 redis
在 Windows 11 上安装 Redis 可以采用几种不同的方法,这里介绍几种常见的方法: 方法 1:使用 Microsoft Store Windows 11 提供了 Microsoft Store,你可以直接从那里安装 Redis。 打开 Microsoft Store。 在搜索框中输入 “Redi…...
5. k8s 之 pod原理与使用
Kubernetes Pod 原理详解 1. Pod 的部署方式 Pod 是 Kubernetes 的最小调度单元,其部署方式分为 声明式(YAML) 和 命令式(kubectl) 两种: (1) 声明式部署(推荐) 通过 YAML 文件定…...
人形机器人动作策略 ∼ 人类动作策略
25年3月来自UCSD、CMU、西雅图 UW、MIT 和 Apple 公司的论文“Humanoid Policy ∼ Human Policy”。 利用多样化数据训练人形机器人的操作策略,可以增强其在跨任务和平台的鲁棒性和泛化能力。然而,仅从机器人演示中学习需要耗费大量的人力,需…...
MySQL事务详解:从5.7到8.0的变化
MySQL事务详解:从5.7到8.0的变化 引言 在关系型数据库管理系统(RDBMS)中,事务是一个核心概念,它确保了数据的一致性和可靠性。MySQL作为最流行的开源RDBMS之一,其事务处理机制在不同的版本中经历了重要的…...
conda常用命令简解
以下是conda常用命令的汇总: 创建一个新环境: conda create -n your_env_name pythonX.X 激活某个环境: activate your_env_name 安装包: conda install [package] 查看安装了哪些包: conda list 查看当前有哪些虚拟环境&…...
数据科学与机器学习:前沿技术研究
数据科学与机器学习:前沿技术研究 摘要 本文探讨了数据科学与机器学习领域的三个前沿方向:自适应机器学习模型、联邦学习隐私与保护以及多模态数据处理。通过理论分析、算法设计和实验验证,展示了这些技术在解决实际问题中的潜力和挑战。自适应机器学习模型能够根据数据变化…...
个人博客测试报告
作者前言 🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂 🎂 作者介绍: 🎂🎂 🎂 🎉🎉🎉…...
Sentinel源码—3.ProcessorSlot的执行过程一
大纲 1.NodeSelectorSlot构建资源调用树 2.LogSlot和StatisticSlot采集资源的数据 3.Sentinel监听器模式的规则对象与规则管理 4.AuthoritySlot控制黑白名单权限 5.SystemSlot根据系统保护规则进行流控 1.NodeSelectorSlot构建资源调用树 (1)Entry的处理链的执行入口 (2…...
datagrip连接mysql问题5.7.26
1.Case sensitivity: plainmixed, delimitedexac Remote host terminated the handshake. 区分大小写:plain混合,分隔exac 远程主机终止了握手。 原因:usessl 参数用于指定是否使用 SSL(Secure Sockets Layer)加密来保护数据传…...