C语言:volatile关键字讲解

 

读音:vaoletail

   C语言中的volatile关键字是一个重要的类型修饰符,它用于声明一个变量具有“易变性”,即可能在编译器无法察觉的情况下被改变其值。Volatile意思是“易变的”,应该解释为“直接存取原始内存地址”比较合适。 “易变”是因为外在因素引起的,像多线程,中断等。

    volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,告诉编译器对该变量不做优化,都会直接从变量内存地址中读取数据,从而可以提供对特殊地址的稳定访问。

    如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。

总结:volatile关键词影响编译器编译的结果,用volatile声明的变量表示该变量随时可能发生变化,与该变量有关的运算,不要进行编译优化,以免出错。

编译器优化介绍:

  由于内存访问速度远不及CPU处理速度,为提高机器整体性能。

1)在硬件上:  引入硬件高速缓存Cache,加速对内存的访问。另外在现代CPU中指令的执行并不一定严格按照顺序执行,没有相关性的指令可以乱序执行,以充分利用CPU的指令流水线,提高执行速度。

2)软件一级的优化:一种是在编写代码时由程序员优化,另一种是由编译器进行优化。编译器优化常用的方法有:将内存变量缓存到寄存器。

由于访问寄存器要比访问内存单元快的多,编译器在存取变量时,为提高存取速度,编译器优化有时会先把变量读取到一个寄存器中;以后再取变量值时就直接从寄存器中取值。但在很多情况下会读取到脏数据,严重影响程序的运行效果。

一般说来,volatile用在如下的几个地方: 
(1)中断服务程序中修改的供其它程序检测的变量,需要加volatile:当变量在触发某中断程序中修改,而编译器判断主函数里面没有修改该变量,因此可能只执行一次从内存到某寄存器的读操作,而后每次只会从该寄存器中读取变量副本,使得中断程序的操作被短路。
 (2)多线程环境:在多线程编程中,如果多个线程共享并修改某个变量,而该变量的改变不受当前执行线程控制(比如由其他线程、中断服务程序或者硬件本身修改),那么这个变量就应该用volatile来修饰,以保证所有线程都能看到最新更新的值。
(3)硬件访问:与硬件交互时,例如访问状态寄存器或其他硬件映射的内存位置,这些位置的内容可能由硬件设备自行更改,而不通过CPU指令直接操作。在这种情况下,硬件寄存器通常需要被声明为volatile,这样每次访问都会得到最新的硬件状态,而非缓存在寄存器中的旧值。

注意:频繁地使用volatile很可能会增加代码尺寸和降低性能,因此要合理的使用volatile。


1). 一个参数既可以是const还可以是volatile吗?解释为什么。 

      一个函数参数可以同时被声明const和volatile。当一个参数既被声明为const又为volatile时,它意味着该参数在函数执行期间不可被修改(通过该函数),但其值可能在任何时间点被硬件、操作系统或其他并发线程等不受当前程序控制的因素改变。

例如,在嵌入式系统中,一个指向设备状态寄存器的指针作为函数参数时,它可以被声明为volatile类型。

void checkDeviceStatus(volatile const uint32_t* statusRegister)
{// 在此函数中,我们不能修改statusRegister指向的内容// 但是我们必须每次都从内存中读取它的实际值,因为它可能随时由外部硬件更改。if (*statusRegister & DEVICE_STATUS_FLAG) {// 处理设备状态}
}

volatile关键字确保编译器不会对statusRegister的内容进行优化,即使多次读取也会每次都直接从内存中读取;而const关键字则禁止函数体内部尝试修改这个寄存器的内容。


2). 一个指针可以是volatile 吗?解释为什么。 

 一个指针不仅可以是volatile,而且在某些情况下必须这样声明。声明为volatile的指针表示其指向的数据可能会被程序外部因素异步更改,即使没有显式的修改语句。

例如,在多线程环境或与硬件交互的场合,某个全局变量可能是通过一个I/O端口或者中断服务程序来更新的,那么指向这个变量的指针就应该声明为volatile。

volatile int *portValue = (volatile int*)0x1000; // 假设0x1000是硬件端口地址// 线程A中读取并处理端口值
while (1) {process(*portValue);
}// 线程B中或中断服务例程中端口值被硬件改写
ISR() {*portValue = readHardwarePort();
}

volatile修饰符告诉编译器不要对portValue指向的内存位置进行优化,每次访问都需要重新读取内存中的实际值,因为硬件可能会在任何时候更新这个值。

????这个部分没看懂。
 


int main(void){int i;i = 1;i = 2;return i;
}

    在Debug模式(无任何优化)下, 生成的32位汇编代码(省略其它代码)为:

mov dword ptr[ebp-4],1
mov dword ptr[ebp-4],2
mov eax,dword ptr[ebp-4]
ret

        可见, 对i的每次赋值都会产生一条汇编语句来对应, 这完全同C程序意思,而当生成模式改成Release(优化被打开; 可能需要在项目设置中钩上"生成调试信息")后,对应如下(省略其它代码)。

mov eax,2
ret

多余的赋值代码都被优化掉了, 这就是编译器的优化措施。把源代码改成如下形式:

int main(void){volatile int i;i = 1;i = 2;return i;
}

    修改后, Debug生成的汇编代码没有变化, 但Release生成的代码却变成了:

        push ecxmov dword ptr[esp],1mov dword ptr[esp],2mov eax,dword ptr[esp]pop ecx

        可见, 加了volatile后, 就算打开优化, 编译器也不会作优化, 每次取值都是重新取值。


参考:

https://www.cnblogs.com/hjh-666/p/11148119.html

https://www.cnblogs.com/memset/archive/2012/12/19/2825530.html

https://www.cnblogs.com/armlinux/archive/2010/09/14/2396918.html

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

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

相关文章

好看的表情壁纸

不定时更新表情壁纸,后期支持头像,wx背景等,个人开发,觉得不错,可前往小程序或者公众号查看

定制红酒:品质保障,从源头做起

云仓酒庄的洒派定制红酒,以其卓着的品质和与众不同的口感,赢得了众多消费者的喜爱。而这种品质的保障,正是从源头上开始的。 在葡萄种植方面,种植者对土壤、气候等自然条件进行严格的筛选和评估,确保葡萄能够在理想的环…

Python的错误和异常,异常的处理

我们平时经常会碰到一些报错信息,特别是对于入门的敲代码选手来说,碰到报错嘎嘎头大,但我们要知道它们是程序执行过程中的常态而非例外。本篇文章,就让我们一起来了解一下错误和异常,在平时编程过程中正确理解和有效处…

android adb 实时画面 和操作

1. 下载 scrcpy 建议 windows10 用户 点击链接下载 不然可能会提示缺少部分 dll https://github.com/Genymobile/scrcpy/releases/download/v2.3.1/scrcpy-win32-v2.3.1.ziphttps://github.com/Genymobile/scrcpy/releases/download/v2.3.1/scrcpy-win32-v2.3.1.zip windo…

Android App开发的自动化测试框架UI Automator使用教程

UI Automator为Android程序的UI开发提供了测试环境,这里我们就来看一下Android App开发的自动化测试框架UI Automator使用教程,需要的朋友可以参考下 Android的自动化测试有很多框架,其中ui automator是google官方提供的黑盒UI相关的自动化测试工具,&am…

【进程和线程】操作系统中的并发执行机制

目录 一、什么是进程(Process)? 进程的管理 进程调度(重点) 二、什么是线程(Thread)? 三、进程和线程的区别与联系 进程(Process) 线程(Thread) 总结比较 一、什么是进程(Process)? 进程和线程是操作系统中一个非常核心的话题&#…

【LeetCode】回溯

labuladong回溯 回溯算法秒杀所有排列-组合-子集问题 回溯 一个回溯问题,实际上就是遍历一棵决策树的过程,树的每个叶子节点存放着一个合法答案。你把整棵树遍历一遍,把叶子节点上的答案都收集起来,就能得到所有的合法答案。 站…

CCF-CSP认证考试 202305-3 解压缩 100分题解

更多 CSP 认证考试题目题解可以前往:CSP-CCF 认证考试真题题解 原题链接: 202305-3 解压缩 时间限制: 5.0s 内存限制: 512.0MB 题目背景 西西艾弗岛运营公司是一家负责维护和运营岛上基础设施的大型企业。在公司内,…

上海晋名室外暂存柜助力新技术皮革制品生产行业安全

本周上海晋名又有一台室外危化品暂存柜项目通过验收,此次项目主要用于新技术皮革制品生产行业油桶、化学品等物资的室外暂存安全。 用户单位创立于2004年,是一家从事新技术皮革制品加工、生产的外资企业。 上海晋名作为一家专注工业安全防护领域&#…

python中医学习服务管理系统flask-django-php-nodejs

随着世界经济信息化、全球化的到来和互联网的飞速发展,推动了各行业的改革。若想达到安全,快捷的目的,就需要拥有信息化的组织和管理模式,建立一套合理、动态的、交互友好的、高效的中医学习服务管理系统。当前的信息管理存在工作…

面向对象的三大特性-----封装、继承、多态(Java篇)

🍁 个人主页:爱编程的Tom💫 本篇博文收录专栏:Java专栏👉 目前其它专栏:c系列小游戏 c语言系列--万物的开始_ 🎉 欢迎 👍点赞✍评论⭐收藏💖三连支持一…

ngrok实现内网穿透

在使用jenkins进行自动化部署时,需要设置github的webhook钩子来触发构建,由于jenkins运行在自己的电脑上,因此需要通过内网穿透来接受http请求。 Install ngrok via Homebrew with the following command: brew install ngrok/ngrok/ngrokP…

Linux基础命令[20]-useradd

文章目录 1. useradd 命令说明2. useradd 命令语法3. useradd 命令示例3.1 不加参数3.2 -d(指定家目录)3.3 -g(指定用户组)3.4 -G(指定附属组)3.5 -p(加密密码)3.6 -e(指…

【WEEK4】 【DAY3】整合SSM框架之功能实现—修改、删除数据【中文版】

2024.3.20 Wednesday 接上文【WEEK4】 【DAY2】整合SSM框架之功能实现—总览、添加数据【中文版】 目录 7.6.修改功能7.6.1.修改BookController.java7.6.2.修改allBook.jsp7.6.3.新建updateBook.jsp7.6.4.修改MyBatis-config.xml7.6.5.运行 7.7.删除功能7.7.1.修改BookContro…

【数据库系统】数据库完整性和安全性

第六章 数据库完整性和安全性 基本内容 安全性;完整性;数据库恢复技术;SQL Server的数据恢复机制; 完整性 实体完整性、参照完整性、用户自定义完整性 安全性 身份验证权限控制事务日志,审计数据加密 数据库恢复 冗余…

JavaWeb:AOP、配置优先级、Bean管理、SpringBoot原理、Maven高级

1 AOP 1.1 基本语法 面向切面编程、面向方面编程&#xff0c;面向特定方法编程 在管理bean对象的过程中&#xff0c;主要通过底层的动态代理机制&#xff0c;对特定的方法进行编程 应用&#xff1a;统计每一个业务方法的执行耗时 xml引入依赖 <!-- AOP-->&l…

音视频开发_FFmpeg基石精讲

FFmpeg 框架 核心组件 libavcodec&#xff1a;一个编解码库&#xff0c;包含了众多的编码器和解码器用于编码和解码音视频流。libavformat&#xff1a;一个封装格式库&#xff0c;用于处理各种音视频封装格式。libavutil&#xff1a;一个工具库&#xff0c;提供了常见功能的简…

牛客周赛 Round 37 E.魔法之森的蘑菇

广搜板子&#xff0c;加个方向就好了 注意多测清空问题 #include<bits/stdc.h> using namespace std; using ll long long; #define int long long const int N 1e510; const int inf 0x3f3f3f3f; const int mod 1e97;int n,m; char g[1010][1010]; bool vis[1010][1…

CI/CD实战-jenkins部署 3

安装 软件下载地址&#xff1a;Index of /jenkins/redhat/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 启动服务 安装推荐插件 不新建用户&#xff0c;使用admin账号登录 修改一下初始密码 新建项目测试 安装git命令 生成密钥 在gitlab中上传公钥 修改ssh 创建中…

蓝桥杯(3.22 刷真题)

P8682 [蓝桥杯 2019 省 B] 等差数列 RE是因为除以0的情况 import java.util.Arrays; import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt();int[] res new int[n1];for(int i1;i&l…