实例观察 c 语言中 volatile 的作用

volatile 意思是易变的。

在 c 语言中,如果变量被 volatile 修饰,就是告诉编译器这个变量随时都可能发生变化,那么每次读取变量的时候都会到内存中读取。

如果变量没有被 volatile 修饰,并且编译器发现在多次读取变量之间,变量没有被修改,那么编译器可能将变量的值保存到寄存器中,这样在后边访问变量的时候性能会得到提升。但是如果变量以编译器无法识别的方式被修改,那么这个时候将变量的值保存在寄存器中就可能引入问题。

1 volatile 使用场景

(1)硬件寄存器映射的内存地址

在嵌入是开发中,cpld 或者 fpga 中的寄存器可以映射到内存地址,直接通过内存地址来访问这些寄存器。这些寄存器的值可能会因硬件原因发生改变,这种情况是编译器无法识别的,所以需要 volatile 进行修饰。

(2)中断服务程序中会修改的,并且其它线程会访问的变量,需要使用 volatile 进行修饰。

(3)多线程应用中,多个线程共享的变量,需要使用 volatile 进行修饰。

volatile 是对编译器优化的补充。

在接触 volatile 之前,总会想当然的认为读取一个变量的时候就会去内存地址中读取。其实不不然,编译器会进行优化,如果在多次读取变量之间没有发现变量被修改,编译器可能将变量的值保存到寄存器中,这样后边的读取直接从寄存器中读取。从寄存器中读取相对于从内存中读取,性能大大提升。

但是编译器优化也不是万能的,当改变变量的方式,编译器无法识别的时候,那么编译器优化会导致问题。比如上边这 3 中场景,在这些场景下使用的变量,就需要使用 volatile 进行修饰。

2 实例观察有无 volatile 的区别

如下代码中,有一个全局变量 int flag,初始值是 1。

创建了两个线程 t1 和 t2,在 t1 中有一个 while() 循环,当 flag 的值为 1 的时候,循环继续;否则循环退出,并打印 "flag is not 1"。在 t2 中,将 flag 修改成了 0。

(1)int flag 被 volatile 修饰

编译命令:

gcc -O3 volatile.c -g

编译之后运行程序,会看到当 flag 被设置为 0 之后,t1 线程中的循环退出,并且打印了 "flag is not 1"。

(2)int flag 没有被 volatile 修饰

还是使用相同的命令进行编译。

编译之后运行程序,当 flag 被设置为 0 之后,看不到 t1 线程中的循环退出。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

volatile int flag = 1;

void *thread_entry(void *data) {
  while (flag == 1) {
    int tmp = flag;
  }
  printf("flag is not 1\n");
}


void *thread_entry1(void *data) {
  int *p = (int *)data;
  *p = 0;
  printf("after set flag to 0\n");
}


int main() {
  pthread_t t1;
  pthread_create(&t1, NULL, thread_entry, NULL);

  sleep(10);
  pthread_t t2;
  pthread_create(&t2, NULL, thread_entry1, &flag);

  sleep(100);
  return 0;
}

通过上边两个实验,我们可以看出来加 volatile 和不加 volatile 的区别,与前边分析的一致。

我们通过将可执行文件进行反汇编,通过汇编指令来分析加 volatile 和不加 volatile 之间的区别。

有 volatile 修饰:

从汇编指令中可以看出来,while() 循环,在 1248 到 1257 指令之间循环执行,每次执行的时候,都会到内存中读取 flag 的值。

无 volatile 修饰:

从汇编指令可以看出,while() 循环中,编译器只在 1234, 123b 两个指令中从内存中读取数据,然后进行比较,后边直接在 123d 这一条指令进行循环,并不是每次循环都从内存中读取数据。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《剑指Offer》笔记题解思路技巧优化 Java版本——新版leetcode_Part_4

《剑指Offer》笔记&题解&思路&技巧&优化_Part_4 &#x1f60d;&#x1f60d;&#x1f60d; 相知&#x1f64c;&#x1f64c;&#x1f64c; 相识&#x1f622;&#x1f622;&#x1f622; 开始刷题1. LCR 148. 验证图书取出顺序——栈的压入、弹出序列2. LCR 14…

CDP和Chrome

CDP和Chrome CDP和WebDriver Protocol WebDriver和 Chrome DevTools Protocol&#xff08;CDP&#xff09; 是用于自动化浏览器的两个主要协议&#xff0c;大多数的浏览器自动化工具都是基于上述其中之一来实现的。可以通过这两种形式来和浏览器交互&#xff0c;通过代码来控…

每日一题——LeetCode1460.通过翻转子数组使两个数组相等

方法一 哈希Map 用两个Map集合分别统计target和arr里出现的元素和出现的次数&#xff0c;在比较两个Map集合看是否出现的元素和次数都相同 var canBeEqual function(target, arr) {let map1 new Map();let map2 new Map();for (let item of target) {map1.set(item, (map1…

利用 pt-archiver 实现数据库归档功能

文章目录 一、前言关于Percona 二、Percona Toolkit安装 percona-toolkit&#xff1a;pt-archiver 归档命令的使用格式&#xff1a;示例&#xff1a; 三、归档步骤&#xff1a;1&#xff09;、创建归档数据库和归档表方式一(推荐)&#xff1a;这种方式的优缺点&#xff1a; 方式…

在script标签写export为什么会抛错|type module import ES5 ES6 预处理 指令序言 JavaScript JS

今天我们进入到语法部分的学习。在讲解具体的语法结构之前&#xff0c;这一堂课我首先要给你介绍一下 JavaScript 语法的一些基本规则。 脚本和模块 首先&#xff0c;JavaScript 有两种源文件&#xff0c;一种叫做脚本&#xff0c;一种叫做模块。这个区分是在 ES6 引入了模块…

【JVM篇】什么是类加载器,有哪些常见的类加载器

文章目录 &#x1f354;什么是类加载器&#x1f6f8;有哪些常见的类加载器 &#x1f354;什么是类加载器 负责在类加载过程中&#xff0c;将字节码信息以流的方式获取并加载到内存当中 &#x1f6f8;有哪些常见的类加载器 启动类加载器 启动类加载器是有Hotspot虚拟机通过的类…

数据结构通讲

目录 集合源码详解 一、常见数据结构讲解 1. 线性数据结构 1.1 数组 1.2 队列 1.3 链表 1.3.1 单向链表 1.3.2 双向链表 1.4 栈 2. 非线性数据结构 2.1 树 2.2 二叉树 2.2.1 概念介绍 2.2.2 遍历操作 2.2.3 删除节点 2.2.4 查找局限性 2.2.5 AVL&#xff08; …

10_Java泛型

一、为什么要有泛型 1.泛型的设计背景 集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象&#xff0c;所以在JDK1.5之前只能把元素类型设计为Object&#xff0c;JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定&#xff0c;其他的部分…

收藏!如何有效实施DevOps?

当今IT行业的竞争日益激烈&#xff0c;各家公司都在寻找优化软件研发过程的方法&#xff0c;因为交付比对手更具竞争力的产品已经越发成为一件成本高昂的事情。这也是DevOps发挥作用的地方&#xff0c;因为它可以在工程管理的各个方面提供帮助。 瀑布开发模型已被广泛使用多年&…

信号系统之神经网络

1 目标检测 科学家和工程师经常需要知道是否存在特定的物体或条件。例如&#xff0c;地球物理学家在地球上探索石油&#xff0c;医生检查病人是否有疾病&#xff0c;天文学家在宇宙中寻找外星智慧&#xff0c;等等。这些问题通常涉及将采集的数据与阈值进行比较。如果超过阈值…

(AtCoder Beginner Contest 341)(A - D)

比赛地址 : Tasks - Toyota Programming Contest 2024#2&#xff08;AtCoder Beginner Contest 341&#xff09; A . Print 341 模拟就好了 &#xff0c; 先放一个 1 , 然后放 n 个 01 ; #include<bits/stdc.h> #define IOS ios::sync_with_stdio(0);cin.tie(0);cout…

探究二维码技术:连接现实与数字世界的桥梁

title: 探究二维码技术&#xff1a;连接现实与数字世界的桥梁 date: 2024/2/19 13:15:36 updated: 2024/2/19 13:15:36 tags: 二维码技术数据编码纠错算法图像处理商业应用安全验证实时交互 引言&#xff1a; 二维码已经成为现代社会中广泛应用的一种技术工具。它不仅在商业领…

前端win10如何设置固定ip(简单明了)

1、右击这个 2、点击属性 3、双击协议版本4设置成以下就ok

在 Python 中,通过列表字典创建 DataFrame 时,若字典的 key 的顺序不一样以及部分字典缺失某些键,pandas 将如何处理?

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ pandas 是一个快速、强大、灵活且易于使用的开源数据分析和处理工具&#xff0c;它是建立在 Python 编程语言之上的。 pandas 官方文档地址&#xff1a;https://pandas.pydata.org/ 在 Python 中&…

【软件设计师】程序猿需掌握的技能——数据流图

作为一个程序员&#xff0c;不仅要具备高水平的程序编码能力&#xff0c;还要是熟练掌握软件设计的方法和技术&#xff0c;具有一定的软件设计能力&#xff0c;一般包括软件分析设计图&#xff08;常见的有数据流图&#xff0c;程序流程图&#xff0c;系统流程图&#xff0c;E-…

华清远见嵌入式学习——驱动开发——作业1

作业要求&#xff1a; 通过字符设备驱动分步注册过程实现LED驱动的编写&#xff0c;编写应用程序测试&#xff0c;发布到CSDN 作业答案&#xff1a; 运行效果&#xff1a; 驱动代码&#xff1a; #include <linux/init.h> #include <linux/module.h> #include &l…

【《高性能 MySQL》摘录】第 3 章 服务器性能剖析

文章目录 3.1 性能优化简介3.1.1 通过性能剖析进行优化3.1.2 理解性能剖析 3.2 对应用程序进行性能剖析3.3 剖析 MySQL 查询3.3.1 剖析服务器负载捕获 MySQL 的查询到日志文件中分析查询日志 3.3.2 剖析单挑查询使用 SHOW PROFILE &#xff08;现已过时&#xff09;使用SHOW ST…

Uiautomator2实现Android自动化测试详解

目录 1、UIautomator2框架原理 2、UIautomator2使用 2.1、安装 2.2、元素定位工具-weditor 2.3、设备连接 2.4、全局配置 2.4.1、通过settings设置 2.4.2、通过属性设置 2.5、APP相关操作 2.5.1、安装应用 2.5.2、启动应用 2.5.3、等待应用启动 2.5.4、结束应用 …

day1 2/18

1> 使用fgets统计给定文件的行数 #include<myhead.h> int main(int argc, const char *argv[]) {if(argc!2){printf("enter error\n");return -1;}FILE*fpNULL;if((fpfopen(argv[1],"r"))NULL){perror("fopen error");return -1;}i…

3D模型素材哪家好?推荐六大优质3D模型资源库!

如今越来越多的设计师在寻找合适的3D模型素材用于设计项目中&#xff0c;帮助自己提高工作效率。然而&#xff0c;市面上的3D模型素材琳琅满目&#xff0c;质量参差不齐。那么&#xff0c;哪家的3D模型素材比较好呢?本文将为你推荐六大优质3D模型资源库&#xff0c;助你轻松找…