Compiler- volatile关键字

news2024/10/6 8:33:23

为了直观的感受编译器为程序所做的编译优化,我们通过以下的C++程序来进行演示(只能体现编译优化的一小部分hh~)。

请大家预测一下下面代码的输出结果

#include <iostream>

int main(void)
{
	const volatile int local = 10;
	int* ptr = (int*)&local;

	printf("Initial value of local : %d \n", local);

	*ptr = 100;

	printf("Modified value of local: %d \n", local);

    return 0;
}

可能大家会想,const修饰的变量怎么还能被修改了呢?(或者是这样想,这不就是通过指针把指向地址的内容给修改了嘛,没有难度呀!)

好,下面我们对此做出解释:

(1)对于持这种观点的读者(const修饰的变量怎么还能被修改了呢?),我们对程序做以下修改(增加一行 local = 20)。

#include <iostream>

int main(void)
{
	const volatile int local = 10;
    // const int local = 10;
	int* ptr = (int*)&local;

	printf("Initial value of local : %d \n", local);

	*ptr = 100;
    local = 20;

	printf("Modified value of local: %d \n", local);

    return 0;
}

然后再进行编译,结果如下。

这说明const还是起作用的,编译报错,说表达式必须是可修改的左值,而现在的这个左值是只读的。而我们这个代码之所以能够修改local,就是因为我们通过使用一个指针ptr(将经过int *类型转换的&local赋给它)的缘故。

(2)对于持这种观点的读者(这不就是通过指针把指向地址的内容给修改了嘛),我们再对代码进行修改(待我把volatile去掉,再运行一下)

#include <iostream>

int main(void)
{
	// const volatile int local = 10;
    const int local = 10;
	int* ptr = (int*)&local;

	printf("Initial value of local : %d \n", local);

	*ptr = 100;

	printf("Modified value of local: %d \n", local);

    return 0;
}

嘿嘿,这个结果是不是比较出乎意料呢?为啥捏?

下面我们来看看volatile关键字的定义

volatile关键字旨在防止编译器对对象进行任何优化,这些对象可能被修改,而这种修改编译器无法确定。
声明为volatile的对象从优化中被省略,因为它们的值可以随时通过当前代码范围之外的代码进行更改。 系统总是从内存位置读取volatile对象的当前值,而不是在请求时将其值保存在临时寄存器中,即使先前的指令要求从同一对象中获取值。

由于访问内存比访问寄存器差不多要慢一个数量级,所以常见的优化有,如果编译器认为一个变量的值在一段时间内不会被修改,那么它就把这个变量从内存保存到寄存器,然后需要用的时候从寄存器(而不是内存)进行读取。

如果一个变量被声明为volatile,这就是在提醒编译器,这个变量不知道啥时候就要被修改了,所以你不要保存它,每次都要读取最新的值。因此,上述代码中的local变量有volatile修饰的时候,一旦被修改,立刻会被反映出来,但如果没有volatile修饰,编译器会认为,这个local变量是const修饰的(是个常量),那么我就没必要去读内存了,直接从寄存器中把这个值读出来就好了,虽然我们此时利用指针ptr修改了内存中的值,但是print的时候,仍然是读取的寄存器中的10。而用volatile就不会出现这种情况,它总是会去读内存中的值。


为了更加深入的了解volatile的作用,我们对上述程序做以下处理:

●    将不用volatile修饰local变量的C++程序(上面的第三个代码块),编译后生成的可执行文件,记为test。

●    将使用volatile修饰local变量的C++程序(上面的第一个代码块),编译后生成的可执行文件,记为testv。

分别对这两个可执行文件反汇编,看看在汇编代码中是如何体现这种区别的。

我们可以看到,在没有volatile修饰的情况下,编译器对程序做了优化,直接把变量local变成0xa(十进制10),所有用到local的地方甚至都没有去寄存器中取,而是直接把0xa(十进制10)拿过来用了。

而在有volatile修饰的情况下,还是老老实实地从内存中取到eax寄存器,然后再把eax寄存器中的值放到esi,所以print出来的是最新的值。

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

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

相关文章

中国人民大学与加拿大女王大学金融硕士国际班——海归学子最强后援

有人脉&#xff0c;成功像坐电梯&#xff1b;没人脉&#xff0c;成功像爬楼梯。这是每一个职场人都有体悟的“潜规则”。对海归同学来说&#xff0c;加入母校校友会是结识优秀人才、迅速拓展本地人脉圈的不二法宝。中国人民大学与加拿大女王大学金融硕士国际班为海归学子提供最…

QMS-云质说质量 - 4 为什么有的质量人不属于质量部?

想管理好质量&#xff0c;首先就要把质量人员放在合适的组织架构中。 对人进行管理&#xff0c;基本原则是&#xff1a;尽量让员工的利益与企业的利益保持同步&#xff0c;虽然无法做到完全重合&#xff0c;但出发点肯定要战略一致。 俗话说“屁股决定脑袋”&#xff0c;因此&a…

【数据结构初阶】第六节.队列的实现

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一、队列的初步认识 二、Java中队列的使用 三、队列的模拟实现 四、力扣刷题演练 4.1 设计循环队列 4.2 用栈实现队列 4.3 最小栈 总结 前言 今天我…

日撸 Java 三百行day36

文章目录 day36 领接表1. 领接表知识点2.结合上图去抽象一个邻接表结点的对象3.广度优先遍历4.深度优先遍历 #说明 闵老师的文章链接&#xff1a; 日撸 Java 三百行&#xff08;总述&#xff09;_minfanphd的博客-CSDN博客 自己也把手敲的代码放在了github上维护&#xff1a;h…

Vue3+Three.js+antvG2实战项目 智慧城市(三)

前言 在网上找了很久都没有找到使用Three.js开发智慧城市的免费文章或者免费视频,自己花了一点时间做了一个纯前端的智慧城市项目。 技术栈都是最新的:vue3vitetypeScriptThreeantv G2 源码分享 源码 模型,天空图盒子链接分享(不想下载源码可以只用下这个)提取码1234 20230424_…

【iOS】—— KVC再学习

KVC 文章目录 KVCKVC常用的四种方法 key和keyPath的区别用法&#xff1a; 批量存值操作批量赋值操作字典模型相互转化KVC的其他方法 KVC原理探索setValue:forKey: 的原理&#xff08;KVC赋值原理&#xff09;valueForKey:的原理&#xff08;KVC取值原理&#xff09; 注意事项 K…

C++数据结构:手撕AVL树

目录 一. 什么是AVL树 二. AVL树的节点定义 三. AVL树的插入操作 3.1 寻找插入位置 3.2 更新平衡因子 3.3 AVL树的旋转调整 3.4 AVL树插入操作的整体实现 四. AVL树的检验 附录&#xff1a;AVL树的实现完整代码 AVL树定义代码 -- AVLTree.h AVL树检验代码 -- test.…

你了解PostProcessor机制吗?

Spring框架对于后置处理器的最佳实践 PostProcessor译为后置处理器&#xff0c;大多数开发人员都使用过springboot对后置处理器的实例级别实践&#xff0c;也就是BeanPostProcessor接口。其实spring还提供了两种容器级别的实践&#xff1a;BeanDefinitionRegistryPostProcesso…

今天试了试chatgpt

今天试了试chatgpt&#xff0c;真是服了 arcade&#xff1f; Arcade是一个Python游戏开发库&#xff0c;它提供了一系列的工具和函数&#xff0c;可以帮助开发者快速地创建2D游戏。以下是Arcade的一些特点&#xff1a; 简单易用&#xff1a;Arcade提供了简单易用的API&#x…

egg3.0连接egg-mongoose插入一条数据、插入多条数据

插入一条数据 app/router.js use strict;/*** param {Egg.Application} app - egg application*/ module.exports app > {const { router, controller } app;router.get(/, controller.home.index);router.get(/role, controller.role.index);router.post(/role/add, co…

【ChatGPT】如何用十分钟部署一个属于自己的chatgpt网站

&#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是Zeeland&#xff0c;全栈领域优质创作者。&#x1f4dd; CSDN主页&#xff1a;Zeeland&#x1f525;&#x1f4e3; 我的博客&#xff1a;Zeeland&#x1f4da; Github主页: Undertone0809 (Zeeland) (github.com)&…

【两个月算法速成】day03-链表

目录 203. 移除链表元素 题目链接 思路 代码 206. 反转链表 题目链接 思路 代码 总结 203. 移除链表元素 题目链接 力扣 思路 如下图所示就是移除链表的过程 但是值得注意的是&#xff0c;移除头节点和其他位置的节点是不一样的&#xff0c;以为头结点前面没有节点。…

每日学术速递4.24

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Collaborative Diffusion for Multi-Modal Face Generation and Editing(CVPR 2023) 标题&#xff1a;多模态人脸生成和编辑的协同扩散 作者&#xff1a;Ziqi Huang, Kelvin C.K. …

Vue3进阶使用详解(node.js、Vue3路由基础项目、axios的使用详细(实现数据分页---前后端分离)、axios加载失败)

Vue3进阶使用详解(node.js、Vue3路由基础项目、axios的使用详细(实现数据分页—前后端分离)、axios加载失败) Vue cli CLI是Commond-Line Interface&#xff0c;翻译为命令界面&#xff0c;又称脚手架。VueCLI是一个官方发布vue.js项目脚手架。使用VueCLI可以快速搭建vue开发…

【IAR工程】STM8S基于ST标准库读取DHT11数据

【IAR工程】STM8S基于ST标准库读取DHT11数据 ✨申明&#xff1a;本文章仅发表在CSDN网站&#xff0c;任何其他网站&#xff0c;未注明来源&#xff0c;见此内容均为盗链和爬取&#xff0c;请多多尊重和支持原创!&#x1f341;对于文中所提供的相关资源链接将作不定期更换。&…

HTTP协议 GET和POST区别 请求响应 Fiddler postman ajax

&#x1f496; 欢迎来阅读子豪的博客&#xff08;JavaEE篇 &#x1f934;&#xff09; &#x1f449; 有宝贵的意见或建议可以在留言区留言 &#x1f4bb; 欢迎 素质三连 点赞 关注 收藏 &#x1f9d1;‍&#x1f680;码云仓库&#xff1a;补集王子的代码仓库 不要偷走我小火…

Mac下nvm安装使用

​欢迎光临我的博客查看最新文章: https://river106.cn 1、简介 nvm 是 Mac 下的 node.js 管理工具。可以通过 nvm 安装和切换不同版本的 node.js。 官网&#xff1a;https://nvm.uihtm.com/ github&#xff1a;https://github.com/nvm-sh/nvm 2、安装 curl -o- https://raw…

移动端适配rem方案

做移动端的适配我们就是要考虑&#xff0c;对于不同大小的手机屏幕&#xff0c;怎么动态改变页面布局中所有盒子的宽度高度、字体大小等。 这个问题我们可以使用相对单位rem。 那么什么是 rem&#xff1f; rem&#xff08;font size of the root element&#xff09;是指相对…

Linux-中断和时间管理(上)

目录 中断的进入过程 中断的进入过程 为方便实验&#xff0c;本章以配套的目标板 FS4412为例来介绍 Linux 的中断子系统&#xff0c;并且编写相应的中断处理程序。FS4412 上的处理器是 SAMSUNG公司的 Exynos4412&#xff0c;该处理器使用的是4核的 Cortex-A9&#xff0c;&…

c++Lambda匿名函数

cLambda匿名函数 &#xff08;1&#xff09; 定义a. [外部变量方位方式说明符]b. (参数)c. mutabled.noexcept/throw()e.->返回值类型f.函数体 2&#xff09;c11中的拉姆达表达式中的&#xff08;&#xff09;可以省略吗 所谓匿名函数&#xff0c;简单地理解就是没有名称的函…