C中volatile总结

news2024/10/5 19:18:19

        在CPU处理过程中,需要将内存中的数据载入到寄存器中才能计算,所以可能涉及到一个问题,如果内存中的数据被更改了,但是寄存器还是使用的旧数据,这样就会造成数据的不同步。

一、volatile关键字的作用

        使用volatile关键字定义变量,就是告诉编译系统这个变量可能会被意想不到的被改变。编译器就不会对变量进行代码优化。编译器在编译代码时,优化器每次遇到这个变量,都会从内存中重新读取内容,而不会使用保存在寄存器里的备份内容。

二、使用volatile的场景

  • 在中断服务程序中修改的,供其它程序检测的变量(非auto),通常需要定义为volatile     

        中断服务可能会频繁进入,当变量被加载到寄存器中,马上就要被使用时,这时又来了一个中断修改了内存中的变量,如果不加volatile,被使用的变量就是寄存器中保存的也即修改之前的。

  • 在多任务环境下,各任务间共享的标志,通常也需要定义为volatile

        这个情形同中断,可能会使数据不同步。

  • 存储器映射的硬件寄存器通常也需要定义为volatile,因为每次对它的读写都可能有不同意义

        这个情形也类似两种,存储器的数据被转移到了硬件寄存器,这时存储器的数据被更改了,但是程序还可能使用的是硬件寄存器中的数据,这也是数据不同步。

        在stm32中,内存被映射到各种外设上,外设有自己的寄存器组,比如GPIO寄存器组

typedef struct
{
  __IO uint32_t CRL;
  __IO uint32_t CRH;
  __IO uint32_t IDR;
  __IO uint32_t ODR;
  __IO uint32_t BSRR;
  __IO uint32_t BRR;
  __IO uint32_t LCKR;
} GPIO_TypeDef;

#define     __IO    volatile

可以看到寄存器都使用了__IO进行修饰,而__IO就是根据volatile定义的一个宏。

三、案例

1、逻辑分析仪

在使用keil 5分析变量的波形时,变量循环从1->0->1,但是波形一直是处理于低,没有起伏。

uint32_t  flag1;

void delay( uint32_t count )
{
    for (; count!=0; count--);
}

int main(void) {

	while(1) {
		flag1 = 1;
		delay( 1000 );
		flag1 = 0;
		delay( 1000 );
	}
}

分析结果如下所示: 

在flag1用volatile修饰之后波形如下所示 :

2、 硬件寄存器

在直接操作寄存器进行输出时,比如引脚拉到了LED上,LED另一端接高电平,引脚输出0是会点亮,当ODR不使用volatile修饰时,下面的操作编译器优化之后可能就只有 

GPIOB->ODR = 0x00000001 这一句代码了,那么灯是不会亮的,但是实际上灯会闪烁的,因为ODR就是用volatile修饰的。

GPIOB->ODR = 0x00000001;
delay(100);
GPIOB->ODR = 0x00000000;
delay(100);
GPIOB->ODR = 0x00000001;

四、面试

volatile 常见的几个面试题

1、一个参数既可以是const还可以是volatile吗?

        可以,针对的角度不同可以这样理解

const 告诉程序员 这是一个常量,不要更改它,在尝试更改时,编译器会报错

volatile告诉编译器,不要对变量做任何优化,直接从内存中读取内容。

2、一个指针可以是volatile 吗?

  可以,指针和普通变量一样,有时也有变化程序的不可控性,比如一个中服务子程序修改一个指向buffer的指针时,即从一个buffer指向另一个buffer,如果不加volatile,面临的问题如同 二、使用volatile的场景 中的一样。

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

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

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

相关文章

中级工程师职称评审中业绩材料具体有哪些呢?甘建二告诉你

十年职称甘建二,一门心思只聊工程师职称评定。 关注我,轻松拿到职称。 中级工程师职称评审中需要准备的评审材料很多,业绩、论文、技术总结、评审表、社保、单位等各类材料,其中难倒大家的就是业绩,业绩到底指的是什么…

【初识Linux】Linux环境配置、Linux的基本指令 一

Linux基本指令一 一、学习前提(环境配置)①安装Xshell和云服务器推荐②Xshell用途如下图③打开Xshell 二、 Linux基本指令①whoami和who指令②pwd、ls、ls -l三个指令ls指令扩充 ③cd指令前提了解有了上面的认识,我们就可以开始cd指令的学习了 ④tree指令…

从零开始的C++(五)

1.类和对象的补充 当对象是const修饰的常量时,形参中的this是隐含的,那么该如何写函数才能传常量对象呢?如果还是按照正常的方式写,则会出现实参是const修饰的,形参没有,出现了权限的扩大,无法…

基于卷积神经网络的法线贴图生成器

在本文中,我们将学习如何训练卷积神经网络从彩色图像生成法线贴图。 推荐:用 NSDT编辑器 快速搭建可编程3D场景 1、数据和工具 我们正着手训练神经网络从彩色图像生成法线贴图。 我们将以“成对”的方式做到这一点。 这意味着我们将显示相应图像的网络对…

程序人生 / 散文分享 / 生活感悟——【追光的日子】《爷爷的12本日历》,若你也共情,欢迎在评论区分享你的故事、观点、感悟和思考!

在一切变好之前,我们总要经历一些不开心的日子,这段日子也许很长,也许只是一觉醒来。有时候,选择快乐,更需要勇气。 🎯作者主页: 追光者♂🔥 🌸个人简介: 💖[1] 计算机专业硕士研究生💖 🌿[2] 2023年城市之星领跑者TOP1(哈尔滨)🌿 🌟[3]…

Spring Cloud OpenFeign 性能优化的4个方法

OpenFeign 是 Spring 官方推出的一种声明式服务调用和负载均衡组件。它的出现就是为了替代已经进入停更维护状态的 Netflix Feign,是目前微服务间请求的常用通讯组件。 1.超时设置 OpenFeign 底层依赖Ribbon 框架,并且使用了 Ribbon 的请求连接超时时间…

打表找规律与分析判断:ARC144C

https://atcoder.jp/contests/arc144/tasks/arc144_c?langen 一开始我猜的结论是前后 k k k 个预处理,中间贪心。 通过打表: 可以发现是前面 2 k 2k 2k 连续块直接暴配,最后一段再用我想的贪心。 究其原因,其实是我们本质上…

安卓开发中遇到的奇奇怪怪的问题(四)

好久没有写这个系列了,感觉还是需要把日常开发中遇到的问题做一个记录总结,因为有些问题我当时遇到时,搜都搜不到,只能慢慢摸索。帮助他人的同时也能给自己留个备忘录。话不多说,凡是近一年的奇怪问题,我想…

Rust中的枚举和模式匹配

专栏简介:本专栏作为Rust语言的入门级的文章,目的是为了分享关于Rust语言的编程技巧和知识。对于Rust语言,虽然历史没有C、和python历史悠远,但是它的优点可以说是非常的多,既继承了C运行速度,还拥有了Java…

24 mysql all 查询

前言 这里主要是 探究一下 explain $sql 中各个 type 诸如 const, ref, range, index, all 的查询的影响, 以及一个初步的效率的判断 这里会调试源码来看一下 各个类型的查询 需要 lookUp 的记录 以及 相关的差异 此系列文章建议从 mysql const 查询 开始看 测试表结构…

【网络】路由器和交换机的区别

🍁 博主 "开着拖拉机回家"带您 Go to New World.✨🍁 🦄 个人主页——🎐开着拖拉机回家_Linux,大数据运维-CSDN博客 🎐✨🍁 🪁🍁 希望本文能够给您带来一定的帮助&#x1…

小谈设计模式(21)—迭代器模式

小谈设计模式(21)—迭代器模式 专栏介绍专栏地址专栏介绍 迭代器模式对象分析聚合对象(Aggregate)迭代器对象(Iterator) Java程序示例程序分析12 优缺点分析优点简化了聚合对象的接口统一的遍历方式增加了代…

20秒基于Chat GPT完成工作中的小程序

1. 写在前面 GPT自从去年爆发以来,各大公司在大模型方面持续发力,行业大模型也如雨后春笋一般发展迅速,日常工作中比较多的应用场景还是问答模式,作为写程序的辅助也偶尔使用。今天看到一篇翻译的博客“我用 ChatGPT,…

更新Xcode 版本后运行项目出现错误 Unable to boot the Simulator 解决方法

错误截图 出现 Unable to boot the Simulator 错误原因很多,以下方法不一定都适用,我是通过以下方法解决的 打开命令终端输入以下命令,可能需要你输入开机密码 sudo rm -rf ~/Library/Developer/CoreSimulator/Caches

Android---字节码层面分析Class类文件

Java 提供了一种可以在所有平台上都能使用的一种中间代码---字节码文件(.class文件)。有了字节码,无论是那个平台只要安装了虚拟机都可以直接运行字节码文件。有了虚拟机,解除了 java 虚拟机与 java 代码之间的耦合。 Java 虚拟机当初被设计出来时就不单…

【设计模式】访问者模式

文章目录 1.访问者模式定义2.访问者模式的角色3.访问者模式实战案例3.1.场景说明3.2.UML类图3.3.代码实现 4.访问者模式优缺点5.访问者模式适用场景6.访问者模式总结 主页传送门:💁 传送 1.访问者模式定义 访问者模式(Visitor Pattern&#x…

【MATLAB源码-第43期】基于matlab的turbo码误码率仿真比较不同迭代次数,采用logmap/sova算法。

操作环境: MATLAB 2022a 1、算法描述 Turbo码是一种前向纠错码 (Forward Error Correction, FEC),在 1993 年由法国的两位研究员 Claude Berrou 和 Alain Glavieux 提出。这种编码技术以其接近 Shannon 极限的高性能而受到广泛关注。以下是关于 Turbo…

黑马JVM总结(二十六)

(1)异常-catch 下面看一下字节码里面怎么做异常的处理 (2)异常-多个catch astore_2:就是把异常对象的地址存到局部变量表2号曹位上 (3)异常-multicatch (4)异常-finally &#xff0…

uniapp 实现地图头像上的水波纹效果

最近实现了uniapp 地图头像水波纹的效果,话不多说,先来看看视频效果吧:链接 在这里具体的代码就不放出来了,还是利用了uniapp的 uni.createAnimation 方法,因为cover-view 不支持一些css 的动画效果,所以这…

探秘布隆过滤器:高效数据查找与去重利器

探秘布隆过滤器:高效数据查找与去重利器 引言 在现代计算机科学中,数据的查找与去重是一个至关重要的问题。本文将介绍一种高效的数据结构——布隆过滤器,它能够在海量数据中快速判断某个元素是否存在,同时具有出色的空间效率。…