关键字volatile作用和用法

news2024/12/29 9:53:30

目录

一、多线程编程中的volatile关键字

二、嵌入式编程中的volatile关键字

三、 优化编译器优化

四、 指针类型转换


        一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。

        精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量在内存中的值,而不是使用保存在寄存器里的备份(虽然读写寄存器比读写内存快)。

        它主要用于处理与多线程、中断处理和硬件寄存器等相关的情况。

  1. 防止编译器优化:编译器在优化代码时会尝试将变量的访问操作优化为更高效的方式,例如将变量的值缓存在寄存器中。然而,对于某些特殊的变量,如多线程环境下的共享变量、中断处理中的标志位、硬件寄存器等,这种优化可能会导致意外的行为。使用 volatile 关键字可以告诉编译器不要对该变量进行优化,确保每次访问都从内存中读取或写入。

  2. 处理多线程共享变量:在多线程编程中,当一个变量被多个线程共享并且可能被一个线程修改时,需要使用 volatile 关键字来确保线程之间的可见性。这样可以防止编译器对共享变量的优化,确保每个线程都能正确地读取到最新的值。

  3. 处理中断和硬件寄存器:在中断处理程序中,某些变量可能由硬件直接修改,而不是通过常规的变量赋值操作。在这种情况下,使用 volatile 关键字可以确保编译器不会对这些变量的访问进行优化,以避免出现不一致的行为。

一、多线程编程中的volatile关键字

        最常见的用途之一是在多线程编程中,通过volatile关键字告知编译器不要对变量进行优化,以避免出现意外的行为。例如,在多线程环境中,一个线程可能会修改某个变量,而另一个线程在不知情的情况下使用了这个变量。下面是一个示例,展示了在多线程编程中使用volatile的情况:

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

volatile int sharedValue = 0;

void *threadFunction(void *arg)
{
    sharedValue = 10;
    return NULL;
}

int main()
{
    pthread_t thread;
    pthread_create(&thread, NULL, threadFunction, NULL);
    while (sharedValue != 10)
    {
        printf("1\n");
        //sleep(1);
    }
    printf("sharedValue has been modified.\n");

    pthread_join(thread,NULL);
    return 0;
}

编译代码:

gcc test_volatile.c -pthread 

 执行代码:

        在这段代码中,volatile关键字的作用是防止编译器对sharedValue变量进行优化。在多线程环境中,一个线程可能会在另一个线程改变sharedValue的值之前,就已经读取了sharedValue的值。如果编译器对sharedValue进行优化,可能会导致一个线程读取到的是旧的、已经被优化过的sharedValue的值,而不是最新的值。

        volatile关键字告诉编译器,sharedValue是一个易变的变量,可能会被程序的其他部分(例如其他线程、硬件设备等)改变。因此,每次引用sharedValue时,都需要直接从它在内存中的地址读取,而不能依赖可能已经过时的、保存在寄存器中的值

        在这段代码中,volatile确保了主线程能够看到子线程对sharedValue的修改。如果没有volatile,编译器可能会认为主线程的循环是无效的(因为它可能认为sharedValue的值没有改变),并可能优化掉这个循环,导致程序的行为不正确。所以,volatile在这里是必要的,以确保程序的正确性。

二、嵌入式编程中的volatile关键字

        在嵌入式编程中,硬件寄存器和内存映射设备常常需要使用volatile关键字,以确保编译器不会对其进行优化,从而保证与硬件的交互是准确的。以下是一个示例,展示了在嵌入式环境中使用volatile关键字的情况:

#include <stdio.h>

#define GPIO_PORT ((volatile unsigned int *)0x12345678)

int main() {
    *GPIO_PORT = 0xFF; // 将端口设置为全高电平

    // 假设在这里进行了一些与硬件相关的操作

    unsigned int value = *GPIO_PORT; // 读取端口的值

    printf("Value read from GPIO_PORT: %u\n", value);

    return 0;
}

 

        这段代码试图直接访问物理地址0x12345678,这个地址是一个GPIO端口的地址。在大多数现代操作系统中,用户空间的程序是不能直接访问物理内存的,这是由操作系统的内存保护机制所决定的。当程序试图直接访问一个它没有权限访问的内存地址时,操作系统会产生一个段错误(Segmentation Fault)。

        在这段代码中,*GPIO_PORT = 0xFF;unsigned int value = *GPIO_PORT;这两行代码都试图直接访问物理地址0x12345678,这是不被允许的,所以会产生段错误。

        如果你想在用户空间的程序中访问硬件,你需要使用特定的系统调用,或者使用某种方法将硬件映射到你的进程的地址空间中。具体的方法取决于你的硬件和操作系统。在Linux中,你可以使用mmap()函数将硬件映射到你的进程的地址空间中。在嵌入式系统中,你可能需要使用特定的库或者驱动程序来访问硬件。

        在这段代码中,volatile关键字的作用是防止编译器对GPIO_PORT进行优化。volatile告诉编译器,GPIO_PORT是一个易变的变量,可能会被程序的其他部分(例如其他线程、硬件设备等)改变。因此,每次引用GPIO_PORT时,都需要直接从它在内存中的地址读取,而不能依赖可能已经过时的、保存在寄存器中的值。

        在这段代码中,volatile确保了对GPIO_PORT的读写操作都是直接对内存进行的,而不会被编译器优化掉。这在硬件编程中是非常重要的,因为硬件的状态可能会在任何时候改变,我们需要确保每次读取或写入硬件时,都是获取或设置的最新的状态。如果没有volatile,编译器可能会认为某些对GPIO_PORT的读写操作是无效的(因为它可能认为GPIO_PORT的值没有改变),并可能优化掉这些操作,导致程序的行为不正确。所以,volatile在这里是必要的,以确保程序的正确性。

三、 优化编译器优化

        在某些情况下,我们可能希望关闭编译器的某些优化,以便更好地进行调试或者对代码进行性能分析。volatile关键字可以在这方面发挥作用。以下是一个示例,展示了如何使用volatile来关闭编译器优化: 

#include <stdio.h>

volatile int debugFlag = 0;

void debugPrint(const char *message) {
    if (debugFlag) {
        printf("Debug: %s\n", message);
    }
}

int main() {
    debugFlag = 1;

    debugPrint("This is a debug message.");

    return 0;
}

        在这段代码中,volatile关键字的作用是防止编译器对debugFlag进行优化。volatile告诉编译器,debugFlag是一个易变的变量,可能会被程序的其他部分(例如其他线程、硬件设备等)改变。因此,每次引用debugFlag时,都需要直接从它在内存中的地址读取,而不能依赖可能已经过时的、保存在寄存器中的值。

        在这段代码中,volatile确保了对debugFlag的读写操作都是直接对内存进行的,而不会被编译器优化掉。这在调试代码时是非常重要的,因为debugFlag的状态可能会在任何时候改变,我们需要确保每次读取或写入debugFlag时,都是获取或设置的最新的状态。如果没有volatile,编译器可能会认为某些对debugFlag的读写操作是无效的(因为它可能认为debugFlag的值没有改变),并可能优化掉这些操作,导致程序的行为不正确。所以,volatile在这里是必要的,以确保程序的正确性。

四、 指针类型转换

        有时候,我们可能需要在指针类型之间进行转换,而编译器会认为这是不安全的操作,从而导致编译错误。使用volatile关键字可以告知编译器,这个类型转换是有意义的,不应该引发错误。以下是一个示例:

#include <stdio.h>

int main() {
    int value = 42;
    int *volatile volatileIntPtr = &value;
    void *voidPtr = (void *)volatileIntPtr;

    int *newValuePtr = (int *)voidPtr;
    printf("New value: %d\n", *newValuePtr);

    return 0;
}

 

        在这个例子中,将int指针转换为void指针,然后再转回int指针。在这种情况下,使用volatile关键字可能会更合适,因为编译器不会对void指针的转换进行优化。

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

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

相关文章

【云原生-K8s】镜像漏洞安全扫描工具Trivy部署及使用

基础介绍基础描述Trivy特点 部署在线下载百度网盘下载安装 使用扫描nginx镜像扫描结果解析json格式输出 总结 基础介绍 基础描述 Trivy是一个开源的容器镜像漏洞扫描器&#xff0c;可以扫描常见的操作系统和应用程序依赖项的漏洞。它可以与Docker和Kubernetes集成&#xff0c;…

shell命令学习(1)——(待完善)

explainshell.com shell统计当前文件夹下的文件个数、目录个数Linux之shell常用命令&#xff08;三&#xff09; sort&#xff08;排序&#xff09;、uniq&#xff08;处理重复字符&#xff09; linux中shell将换行输入到文件中 shell脚本&#xff0c;将多行内容写入文件中 f…

springboot086靓车汽车销售网站

springboot086靓车汽车销售网站 成品项目已经更新&#xff01;同学们可以打开链接查看&#xff01;需要定做的及时联系我&#xff01;专业团队定做&#xff01;全程包售后&#xff01; 2000套项目视频链接&#xff1a;https://pan.baidu.com/s/1N4L3zMQ9nNm8nvEVfIR2pg?pwd…

Javascript编程进阶 – 预定义函数

Javascript编程进阶 – 预定义函数 JavaScript Programming Advanced – Predefined Functions By JacksonML JavaScript引擎中包含了一组built-in functions(内建函数)。 本文简要介绍如何通过实践使用这些预定义函数并掌握传递参数和返回值。希望对您有所帮助。 JavaScri…

如何使用HadSky搭配内网穿透工具搭建个人论坛并发布至公网随时随地可访问

文章目录 前言1. 网站搭建1.1 网页下载和安装1.2 网页测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;2.3 Cpolar稳定隧道&#xff08;本地设置&#xff09;2.4 公网访问测试 总结 前言 经过多年的基础…

【我爱C语言】详解字符函数isdigit和字符串转换函数(atoi和snprintf实现互相转换字符串)三种strlen模拟实现

&#x1f308;write in front :&#x1f50d;个人主页 &#xff1a; 啊森要自信的主页 ✏️真正相信奇迹的家伙&#xff0c;本身和奇迹一样了不起啊&#xff01; 欢迎大家关注&#x1f50d;点赞&#x1f44d;收藏⭐️留言&#x1f4dd;>希望看完我的文章对你有小小的帮助&am…

使用Python实现轮盘赌选择法Roulette Wheel Selection Method in Python

一、引言 最近在手写遗传算法&#xff0c;想尝试解决一些优化问题。然而&#xff0c;在编码的过程中&#xff0c;自己发现了很多都不懂的问题。比如&#xff0c;交叉的操作&#xff0c;有单点交叉、两点交叉和多点交叉&#xff0c;具体选哪一种会更好呢&#xff1f;未知。还有交…

异常检测 | 基于孤立森林(Isolation Forest)的数据异常数据检测(结合t-SNE降维可视化)

异常检测 | MATLAB实现基于孤立森林的数据异常检测 目录 异常检测 | MATLAB实现基于孤立森林的数据异常检测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 Matlab实现基于孤立森林(Isolation Forest)的数据异常数据检测可视化&#xff08;完整源码和数据) 基于孤立森林(…

好用免费的AI换脸5个工具

在当今社会的发展中&#xff0c;人工智能&#xff08;Artificial Intelligence, AI&#xff09;扮演着关键的角色&#xff0c;其应用领域不断扩展。作为AI的一个分支&#xff0c;换脸技术近年来备受欢迎。这项技术使得将一个人的面部特征迁移到另一个人的照片或视频成为可能。除…

【React】路由的基础使用

react-router-dom6的基础使用 1、安装依赖 npm i react-router-dom默认安装最新版本的 2、在src/router/index.js import { createBrowserRouter } from "react-router-dom"/* createBrowserRouter&#xff1a;[/home]--h5路由createHashRouter&#xff1a;[/#/ho…

各种滤波算法的比较(GF、KF、EKF、UKF、PF),内附简单实现代码

目录 一、前言 二、滤波算法介绍 1、GF&#xff08;高斯滤波&#xff09; 2、KF&#xff08;卡尔曼滤波&#xff09; 3、EKF&#xff08;可扩展卡尔曼滤波&#xff09; 4、UKF&#xff08;无迹卡尔曼滤波&#xff09; 5、PF&#xff08;粒子滤波&#xff09; 三、不同滤…

zabbix配置snmp trap--使用snmptrapd和Bash接收器--图文教程

1.前言 我的zabbix的版本是5.0版本&#xff0c;5.0的官方文档没有使用bash接收器的示例&#xff0c;6.0的官方文档有使用bash接收器的示例&#xff0c;但是&#xff0c;下载文件的链接失效&#xff1f;&#xff01; 这里讲解zabbix-server端配置和zabbix web端配置 2.zabbix-…

Day04 Liunx高级系统设计4-信号

进程间通讯 引入 如何将 A 进程中的数据传入 B 进程呢 ? 我们要使用进程间通讯 概述 中文名 : 进程间通讯 英文名 :IPC 英文全称 :Inter Processes Communication 作用: 数据传输&#xff1a;一个进程需要将他的数据发送给另一个进程】 资源共享&#xff1a;多个进程可以…

排序:直接选择排序

直接选择排序&#xff1a; 本质&#xff1a; 直接选择排序的本质就是在数组中进行遍历挑选出最大的元素&#xff0c;讲最大的元素放到对应的位置后&#xff0c;再次选出次大的位置&#xff0c;而后又放到对应的位置..........................直到数组成为一个有序序列。 优…

二叉树的层平均值[中等]

优质博文&#xff1a;IT-BLOG-CN 一、题目 给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[3.00000,14.50000,1…

解决:AttributeError: module ‘copy’ has no attribute ‘copy’

解决&#xff1a;AttributeError: module ‘copy’ has no attribute ‘copy’ 文章目录 解决&#xff1a;AttributeError: module copy has no attribute copy背景报错问题报错翻译报错位置代码报错原因解决方法方法一方法二方法三今天的分享就到此结束了 背景 在使用之前的代…

C语言指针详解上

1 野指针 int main01(){//野指针就是没有初始化的指针,指针的指向是随机的,不可以 操作野指针//int a 0;//指针p保存的地址一定是定义过的(向系统申请过的)int *p;//野指针*p 200;printf("%d\n",*p);system("pause");return 0;}2 空指针 空指针的作用…

Unity 关于Ray、RaycastHit、Raycast及其使用

Unity中&#xff0c;我们要进行物理模拟和碰撞检测时&#xff0c;有三个重要的概念Ray、RaycastHit、Raycast。 其中&#xff0c;Ray可以理解为射线&#xff0c;它是一条从起点沿着特定方向延伸的无限长线段。 它的语法是&#xff1a; Ray(Vector3 origin, Vector3 directio…

使用阿里巴巴同步工具DataX实现Mysql与ElasticSearch(ES)数据同步

一、Linux环境要求 二、准备工作 2.1 Linux安装jdk 2.2 linux安装python 2.3 下载DataX&#xff1a; 三、DataX压缩包导入&#xff0c;解压缩 四、编写同步Job 五、执行Job 六、定时更新 6.1 创建定时任务 6.2 提交定时任务 6.3 查看定时任务 七、增量更新思路 一、Linux环境要…

el-table操作栏按钮过多 增加展开/收起功能

是的 如图所示有那么一条数据 列表操作栏的按钮七八个 小屏笔记本啥数据项也别看了 就剩下个固定列大刺刺的占着整个页面 解决方法&#xff1a; <el-table-column :width"tableToggle ? 600 : 300" label"操作栏" align"center" header-ali…