【C++ 关键字 类型限定符 】揭秘C++编程中的神秘元素:深入了解volatile关键字的强大作用

简介: 【C++ 关键字 类型限定符 】揭秘C++编程中的神秘元素:深入了解volatile关键字的强大作用


📌volatile 关键字作用:防止编译器过度优化,指示易变的变量可能在程序外部被修改,确保顺序执行和及时更新。

📌字面意思就是易变的、不可优化的、顺序执行的。

volatile关键字的介绍

由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。

C/C++中volatile用来修饰一个变量,表示这个变量可以被编译器之外的东西改变。

  • 说明
    volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。
    如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值;
    如果这个变量由别的程序更新了的话,将出现不一致的现象。

volatile 可以修饰的范围以及限制

在C++中,volatile关键字可以修饰几乎所有的数据类型,包括基本类型(如intcharfloat等)、指针、类对象、结构体、联合体等。以下是一些例子:

  1. 基本类型volatile int a;volatile float b;
  2. 指针volatile int *p;int * volatile p;volatile int * volatile p;(这三个例子分别表示指向volatile int的指针,volatile指针指向intvolatile指针指向volatile int
  3. 类对象:如果一个类的对象被声明为volatile,那么只有类中被声明为volatile的成员函数才能被调用。
  4. 结构体和联合体volatile struct S { ... };volatile union U { ... };

然而,volatile关键字不能与某些其他关键字一起使用。例如,volatile不能与constexpr一起使用,因为constexpr表示的是一个在编译时就能确定的常量,而volatile表示的是一个可能在任何时候被更改的变量。这两个关键字表示的是两种完全不同的概念,因此不能一起使用。

此外,volatile也不能与const一起使用,因为const表示的是一个不能被更改的变量,而volatile表示的是一个可能在任何时候被更改的变量。这两个关键字表示的是两种完全不同的概念。

volatile关键字的使用场景

  • 并行设备的硬件寄存器

存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;


  • 一个中断服务子程序中会访问到的非自动变量(全局变量)

中断服务程序中修改的供其它程序检测的变量需要加volatile


  • 多线程应用中被几个任务共享的变量

多任务环境下各任务间共享的标志应该加volatile


然而,需要注意的是,虽然volatile可以防止编译器进行某些类型的优化,但它并不能提供多线程环境下的同步机制。也就是说,即使一个变量被声明为volatile,在多线程环境中也不能保证对这个变量的访问是原子的。为了实现这种同步,我们需要使用诸如互斥锁(mutexes)、信号量(semaphores)、条件变量(condition variables)等同步原语。

在C++中,std::mutexstd::condition_variable,以及std::atomic类型的变量都是设计用来在多线程环境中安全使用的,它们内部已经处理了所有必要的内存屏障和同步操作,所以你不需要(也不应该)将它们声明为volatile

volatile关键字主要用于防止编译器优化,它告诉编译器这个变量可能会在编译器看不到的地方被修改。然而,对于std::mutexstd::condition_variable,以及std::atomic类型的变量,它们的实现已经考虑到了这一点,所以你不需要使用volatile关键字。

实际上,如果你将这些类型的变量声明为volatile,可能会导致编译器产生错误或警告,因为这可能会让编译器认为你想要做一些不寻常的事情。

所以,总的来说,你不需要(也不应该)在多线程编程中使用volatile关键字。你应该使用std::mutexstd::condition_variable,以及std::atomic类型的变量来实现多线程同步。


volatile关键字的作用

  • 不会在两个操作之间把volatile变量缓存在寄存器中。在多任务、中断、甚至setjmp环境下,变量可能被其他的程序改变,编译器自己无法知道,volatile就是告诉编译器这种情况.
  • 不做常量合并、常量传播等优化.
  • 对volatile变量的读写不会被优化掉。如果你对一个变量赋值但后面没用到,编译器常常可以省略那个赋值操作,然而对Memory Mapped IO的处理是不能这样优化的。

volatile与mutable关键字的区别

  • mutable只能用与类变量,不能与const同时使用;在const修饰的方法中,mutable变量数值可以发生改变;
  • volatile只是运行期变量的值随时可能改变,这种改变即可能来自其他线程,也可能来自外部系统。

volatile与互斥量

  • volatile关键字在多线程编程中可以用于共享变量,但它本身并不能提供线程安全的保证。volatile关键字只能确保编译器不会对该变量进行优化,从而确保每次访问都会从内存中重新读取值。然而,它并不能防止多个线程同时访问和修改共享变量时导致的数据竞争。
  • 互斥量、原子操作和其他同步机制在多线程编程中更常用,因为它们可以确保线程安全。使用互斥量可以保证一次只有一个线程访问共享资源,从而避免数据竞争。原子操作可以保证某些特定的操作是不可分割的,这也有助于避免数据竞争。
    在某些特定情况下,volatile关键字和互斥量、原子操作等同步机制可以一起使用。例如,当您需要在多线程环境中访问硬件寄存器时,可以使用volatile关键字和互斥量一起确保线程安全和避免编译器优化。然而,在大多数情况下,仅使用互斥量、原子操作等同步机制就足以实现线程安全。

总之,虽然volatile关键字在多线程编程中有其用途,但由于它不能提供线程安全保证,因此在实际编程中,开发者更倾向于使用互斥量、原子操作和其他同步机制来实现线程安全。

另言

有时候没有volatile也可能能正常运行,但是可能修改了编译器的优化级别之后就又不能正常运行了。
因此经常会出现debug版本正常,但是release版本却不能正常的问题。所以为了安全起见,只要是等待别的程序修改某个变量的话,就加上volatile关键字。


volatile总是与优化有关,编译器有一种技术叫做数据流分析,分析程序中的变量在哪里赋值、在哪里使用、在哪里失效,分析结果可以用于常量合并,常量传播等优化,进一步可以死代码消除。
但有时这些优化不是程序所需要的,这时可以用volatile关键字禁止做这些优化,volatile的字面含义是易变的,

总结

volatile关键字确实可以用于防止编译器优化,但它本身并不能保证线程安全。在实际编程中,开发者通常会使用互斥量、原子操作等同步机制来确保线程安全。在某些特殊情况下,volatile关键字可以与这些同步机制一起使用。

在我们的编程学习之旅中,理解是我们迈向更高层次的重要一步。然而,掌握新技能、新理念,始终需要时间和坚持。从心理学的角度看,学习往往伴随着不断的试错和调整,这就像是我们的大脑在逐渐优化其解决问题的“算法”。

这就是为什么当我们遇到错误,我们应该将其视为学习和进步的机会,而不仅仅是困扰。通过理解和解决这些问题,我们不仅可以修复当前的代码,更可以提升我们的编程能力,防止在未来的项目中犯相同的错误。

我鼓励大家积极参与进来,不断提升自己的编程技术。无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力。

目录
相关文章
|
1月前
|
存储 C++ UED
【实战指南】4步实现C++插件化编程,轻松实现功能定制与扩展
本文介绍了如何通过四步实现C++插件化编程,实现功能定制与扩展。主要内容包括引言、概述、需求分析、设计方案、详细设计、验证和总结。通过动态加载功能模块,实现软件的高度灵活性和可扩展性,支持快速定制和市场变化响应。具体步骤涉及配置文件构建、模块编译、动态库入口实现和主程序加载。验证部分展示了模块加载成功的日志和配置信息。总结中强调了插件化编程的优势及其在多个方面的应用。
231 64
|
1月前
|
安全 程序员 编译器
【实战经验】17个C++编程常见错误及其解决方案
想必不少程序员都有类似的经历:辛苦敲完项目代码,内心满是对作品品质的自信,然而当静态扫描工具登场时,却揭示出诸多隐藏的警告问题。为了让自己的编程之路更加顺畅,也为了持续精进技艺,我想借此机会汇总分享那些常被我们无意间忽视却又导致警告的编程小细节,以此作为对未来的自我警示和提升。
89 5
|
1月前
|
存储 搜索推荐 C++
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器2
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
48 2
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器2
|
1月前
|
安全 程序员 编译器
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
81 11
|
1月前
|
存储 C++ 容器
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器1
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
52 5
|
1月前
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
41 0
C++入门6——模板(泛型编程、函数模板、类模板)
|
1月前
|
算法 编译器 C++
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
77 2
|
1月前
|
存储 编译器 C++
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
38 2
|
1月前
|
程序员 C++
C++编程:While与For循环的流程控制全解析
总结而言,`while`循环和 `for`循环各有千秋,它们在C++编程中扮演着重要的角色。选择哪一种循环结构应根据具体的应用场景、循环逻辑的复杂性以及个人的编程风格偏好来决定。理解这些循环结构的内在机制和它们之间的差异,对于编写高效、易于维护的代码至关重要。
52 1
|
1月前
|
自然语言处理 编译器 Linux
【C++】巧用缺省参数与函数重载:提升编程效率的秘密武器
【C++】巧用缺省参数与函数重载:提升编程效率的秘密武器