浅析volatile关键字

浅析volatile关键字

文章目录

  • 浅析volatile关键字
    • 1. volatile关键字的意义
    • 2.volatile应用
    • 3. volatile常见问题
    • 总结

1. volatile关键字的意义

​ 被 volatile 修饰的变量,在对其进行读写操作时,会引发一些可观测的副作用。而这些可观测的副作用,是由程序之外的因素决定的

2.volatile应用

(1)并行设备的硬件寄存器(如状态寄存器)。
假设要对一个设备进行初始化,此设备的某一个寄存器为0xff800000。

int *output = (unsigned int *)0xff800000; //定义一个IO端口;  
int init(void)  
{  int i;  for(i = 0; i < 10; i++){  *output = i;  }  
}

经过编译器优化后,编译器认为前面循环半天都是废话,对最后的结果毫无影响,因为最终只是将 output 这个指针赋值为 9,所以编译器最后给你编译编译的代码结果相当于:

int init(void)  
{  *output = 9;  
}

如果你对此外部设备进行初始化的过程是必须是像上面代码一样顺序的对其赋值,显然优化过程并不能达到目的。反之如果你不是对此端口反复写操作,而是反复读操作,其结果是一样的,编译器在优化后,也许你的代码对此地址的读操作只做了一次。然而从代码角度看是没有任何问题的。这时候就该使用volatile通知编译器这个变量是一个不稳定的,在遇到此变量时候不要优化。

(2)一个中断服务子程序中访问到的变量

static int i = 0;int main()
{while(1){if(i) dosomething();}
}/* Interrupt service routine */
void IRS()
{i = 1;
}

上面示例程序的本意是产生中断时,由中断服务子程序IRS响应中断,变更程序变量i,使在 main 函数中调用dosomething 函数,但是,由于编译器判断在 main 函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次 if 判断都只使用这个寄存器里面的“i副本”,导致 dosomething 永远不会被调用。如果将变量i加上 volatile 修饰,则编译器保证对变量i的读写操作都不会被优化,从而保证了变量i被外部程序更改后能及时在原程序中得到感知。

(3)多线程应用中被多个任务共享的变量。
当多个线程共享某一个变量时,该变量的值会被某一个线程更改,应该用 volatile 声明。作用是防止编译器优化把变量从内存装入CPU寄存器中,当一个线程更改变量后,未及时同步到其它线程中导致程序出错。volatile的意思是让编译器每次操作该变量时一定要从内存中真正取出,而不是使用已经存在寄存器中的值。示例如下:

volatile  bool bStop = false;  //bStop 为共享全局变量  
//第一个线程
void threadFunc1()
{...while(!bStop){...}
}
//第二个线程终止上面的线程循环
void threadFunc2()
{...bStop = true;
}

要想通过第二个线程终止第一个线程循环,如果 bStop 不使用 volatile 定义,那么这个循环将是一个死循环,因为 bStop 已经读取到了寄存器中,寄存器中bStop的值永远不会变成 FALSE,加上 volatile,程序在执行时,每次均从内存中读出 bStop 的值,就不会死循环了。

3. volatile常见问题

首先我来逐步抛出三个问题:

(1)一个参数既可以是const还可以是volatile吗?为什么?

可以。一个例子是只读的状态寄存器。它是 volatile 因为它可能被意想不到地改变。它是 const 因为程序不应该试图去修改它。

​ 现在我相应的举一个实际编程中可能会出现的简化案例:(通过指向const变量的指针,对指针解引用修改指向空间的值)

对于有无volatile关键字修饰可能会有两种写法:

void Con_Vol_variable()
{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);
}void Con_NonVol_variable()
{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);
}

注意上面两个函数仅在 local 变量定义时变量类型修饰词有所差异,于是产生了截然不同的运行结果:

在这里插入图片描述

可以看到有 volatile 修饰的变量通过修改 *ptr 的值成功改变了 const 变量的值。

(2)一个指针可以是volatile吗?为什么?

可以。尽管这并不常见。一个例子是当一个中断服务子程序修改一个指向一个buffer的指针时。

(3)下面的函数有什么错误?

int square(volatile int *ptr) 
{ 
return *ptr * *ptr; 
} 

这段代码有点变态,其目的是用来返回指针 ptr 指向值的平方,但是,由于 ptr 指向一个 volatile 型参数,编译器将产生类似下面的代码:

int square(volatile int *ptr) 
{ int a, b; a = *ptr; b = *ptr; return a * b; 
} 

由于*ptr的值可能被意想不到地改变,因此 a和b可能是不同的。结果,这段代码可能返回的不是你所期望的平方值!正确的代码如下:

long square(volatile int *ptr) 
{ int a = *ptr; return a * a; 
} 

总结

  • volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素(操作系统、硬件、其它线程等)更改。所以使用 volatile 告诉编译器不应对这样的对象进行优化。

  • volatile 关键字声明的变量,每次访问时都必须从内存中取出值(没有被 volatile 修饰的变量,可能由于编译器的优化,从 CPU 寄存器中取值)

  • const 可以是 volatile(如只读的状态寄存器)

  • 指针可以是 volatile

在这里插入图片描述

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

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

相关文章

sql单表运用11.3

一、进入数据库操作界面 1、mysql -u root -p 敲回车 &#xff0c;输入密码 &#xff0c;进入数据库操作界面 2、show databases 查看所有的数据&#xff08;如果没有数据库&#xff1a;创建数据库 create database 库名称&#xff09; 3、use 数据库名 使…

探索Terraform实践:优化基础设施管理

Terraform 是管理基础设施及代码&#xff08;IaC&#xff09;最常用的工具之一&#xff0c;它能使我们安全且可预测地对基础设施应用更改。 Terraform作为一个强大的基础设施即代码工具&#xff0c;为开发人员和运维团队提供了一种简单而强大的方式来定义、部署和管理基础设施。…

软件工程顶会——ICSE '24 论文清单、摘要

1、A Comprehensive Study of Learning-based Android Malware Detectors under Challenging Environments 近年来&#xff0c;学习型Android恶意软件检测器不断增多。这些检测器可以分为三种类型&#xff1a;基于字符串、基于图像和基于图形。它们大多在理想情况下取得了良好的…

Go-gin-example 第四部分 重启服务

文章目录 知识点本节目标何谓优雅 ctrlc信号 修改流程实现优雅重启endless安装 编写验证编译执行唤醒问题 续接 上一节 知识点 信号量的了解应用热更新 本节目标 在前文中&#xff0c;我们在配置玩之后直接使用 ctrlc 来进行进程的结束&#xff0c;我们将了解 ctrlc 的过程…

HarmonyOS | 状态管理(九) | Environment (设备环境查询)

系列文章目录 1.HarmonyOS | 状态管理(一) | State装饰器 2.HarmonyOS | 状态管理(二) | Prop装饰器 3.HarmonyOS | 状态管理(三) | Link装饰器 4.HarmonyOS | 状态管理(四) | Provide和Consume装饰器 5.HarmonyOS | 状态管理(五) | Observed装饰器和ObjectLink装饰器 6.Harmo…

为啥要用C艹不用C?

在很多时候&#xff0c;有人会有这样的疑问 ——为什么要用C&#xff1f;C相对于C优势是什么&#xff1f; 最近两年一直在做Linux应用&#xff0c;能明显的感受到C带来到帮助以及快感 之前&#xff0c;我在文章里面提到环形队列 C语言&#xff0c;环形队列 环形队列到底是怎么回…

NLP_文本数据分析_3(代码示例)

目标 了解文本数据分析的作用.掌握常用的几种文本数据分析方法. 1 文件数据分析介绍 文本数据分析的作用: 文本数据分析能够有效帮助我们理解数据语料, 快速检查出语料可能存在的问题, 并指导之后模型训练过程中一些超参数的选择. 常用的几种文本数据分析方法: 标签数量分布句…

阿里云的ssh的22端口,修改为2422端口,作为默认的ssh端口

要将阿里云&#xff08;或任何Linux服务器&#xff09;的SSH端口从默认的22端口更改为2422端口&#xff0c;你可以按照以下步骤操作&#xff1a; 编辑SSH配置文件&#xff1a; 打开SSH服务的配置文件/etc/ssh/sshd_config。你可以使用nano、vi或任何文本编辑器来编辑这个文件。…

Vue3_2024_4天【computer、watch、method在Vue2~3中的说明】未完待补

第一&#xff1a;从概念上介绍~~vue中计算属性、方法、监听器&#xff08;以Vue2描述&#xff09; 1.计算属性 (Computed Properties): 1.概念&#xff1a; 计算属性是基于响应式依赖进行缓存的属性&#xff0c;只有在相关依赖发生改变时才会重新求值。它们类似于具有缓存的函…

场景问题: VisualVM工具Profiler JDBC不是真实执行的SQL

1. 问题 诡异的问题表象&#xff1a; 前端反馈分页接口的Total字段一直为0 使用Visualvm中的 Profiler 注入到应用后&#xff0c;查看JDBC监控得到了分页接口执行的SQL&#xff0c;复制出来执行是55. 此时还没有注意到 IN 的范围中有一个特别的值 NULL &#x1f928; 2. 排查…

视觉Transformers中的位置嵌入 - 研究与应用指南

视觉 Transformer 中位置嵌入背后的数学和代码简介。 自从 2017 年推出《Attention is All You Need》以来&#xff0c;Transformer 已成为自然语言处理 (NLP) 领域最先进的技术。 2021 年&#xff0c;An Image is Worth 16x16 Words 成功地将 Transformer 应用于计算机视觉任务…

Windows C++:控制新进程的创建方式

目录 介绍 标志位介绍 代码示例 这些宏定义&#xff08;dwCreationFlag值&#xff09;是用于Windows操作系统中CreateProcess函数的标志&#xff0c;它们控制新进程的创建方式。下面是这些标志的中文介绍&#xff1a; 介绍 Winbase.h中的部分代码&#xff1a; // // Proc…

idea中引入新JDK环境

在不同的项目中往往会需要不同的运行环境&#xff0c;那么如何下载一个新的环境并运用到idea中呢&#xff1f; 下面给出的就是oracle官网&#xff0c;以JDK17为例教大家如何下载 Java Archive Downloads - GraalVM for JDK 17https://www.oracle.com/java/technologies/javase…

Python数据可视化库之bashplotlib使用详解

概要 在数据可视化领域,Python拥有许多优秀的库,如Matplotlib、Seaborn等,它们可以创建漂亮而复杂的图形。但是,有时候我们可能需要在终端中绘制简单的图形,这时候Bashplotlib就派上了用场。Bashplotlib是一个Python库,可以在终端中绘制基本的图形,如条形图、散点图等。…

YOLOV9训练集制作+Train+Val记录

一、YOLO数据集格式分布 在YOLO中&#xff0c;数据集的分布如图&#xff0c;在dataset文件夹下有imags&#xff08;图片&#xff09;和labels&#xff08;标签&#xff09;。在images和labels文件夹下又分别存放三个文件夹&#xff0c;分别对应测试集、训练集、验证集&#xff…

记一次异步转同步的经历

工作中会经常遇到一些对数据进行加工的场景&#xff0c;这些数据来自很多地方&#xff0c;一般通过HTTP、RPC等方式去调用&#xff0c;数据源返回的报文也一般都是JSON、XML等格式。其中大部分数据源是同步返回&#xff0c;但有些数据源是异步返回的(也就是说数据源的数据是数据…

Android 音乐播放器(暂停、下一首、上一首)

1.编写主页面&#xff0c;使用listview组件放置音乐列表信息 <?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.android.com/apk/r…

使用postman测试若依其他业务接口API—3

请求方式 如上&#xff0c;使用Get请求来获取练习题库中的所有习题数据。 请求地址 在请求路径栏输入请求地址&#xff0c;以下图为例&#xff1a; 参数体与鉴权 在Parms键入所需参数&#xff0c;其中key为键,value为键的值&#xff1a;如下图所示&#xff1a; 认证成功与失…

持续集成(CICD)- gogs仓库的部署和使用

文章目录 一、gogs的介绍二、部署gog仓库三、首次启动gogs四、登录五、创建一个非空仓库六、从仓库拉取代码到本地七、把本地编辑的代码上传到仓库 一、gogs的介绍 Gogs作为一个轻量级、易于部署和使用的自托管Git服务&#xff0c;为小型团队和个人开发者提供了一个简单而强大…

【AIGC】如何提高Prompt准确度

前言 随着人工智能的迅猛进展&#xff0c;AIGC&#xff08;通用人工智能聊天工具&#xff09;已成为多个行业中不可或缺的自然语言处理技术。Prompt作为AIGC系统的一项关键功能&#xff0c;在工具的有效运作中发挥了举足轻重的作用。本篇文章将深入探讨Prompt与AIGC之间的紧密…