当前位置: 首页 > article >正文

volatile变量

volatile 关键字用于告诉编译器,变量的值可能会在程序的控制流之外被修改,因此编译器不能对其进行优化,必须每次访问该变量时都重新读取它的值

使用 volatile 的典型场景

  1. 硬件寄存器访问:在嵌入式编程中,变量可能映射到硬件寄存器,它的值可以随时改变。
  2. 中断服务程序(ISR)中的变量:中断程序可能会修改某些全局变量,而主程序也会访问这些变量。
  3. 多线程共享变量:多线程程序中,多个线程可能会访问和修改同一个变量,使用 volatile 防止编译器对它优化。

示例代码

下面是这三种情况下 volatile 变量的使用示例。

1. 并行设备的硬件寄存器(如状态寄存器)

假设一个嵌入式系统中有一个硬件寄存器地址 0x4000,用来表示设备的状态。

#include <stdio.h>

#define STATUS_REGISTER ((volatile unsigned int *)0x4000) // 假设硬件寄存器地址

int main() {
    // 假设这是一个不断读取设备状态的循环
    while (*STATUS_REGISTER != 0) {
        // 检查设备状态寄存器
        printf("Waiting for device ready...\n");
    }
    printf("Device is ready.\n");

    return 0;
}

在这个例子中,STATUS_REGISTER 是一个指向 volatile 的指针。每次读取该寄存器的值时都要从寄存器读取最新值,而不是使用任何缓存的值。

2. 中断服务程序中的 volatile 变量

假设我们有一个全局变量 timer_expired,在中断服务程序(ISR)中修改它。

#include <stdio.h>
#include <stdbool.h>

volatile bool timer_expired = false; // 标记计时器是否已到期

void timer_interrupt_handler() {
    timer_expired = true; // 中断服务程序将变量设置为true
}

int main() {
    // 主程序中定期检查timer_expired状态
    while (!timer_expired) {
        // 等待计时器中断发生
    }
    printf("Timer expired!\n");

    return 0;
}

在这个例子中,timer_expired 是一个 volatile 变量,因为它会在中断服务程序中被修改,而主程序也会访问它。将它声明为 volatile,可以确保主程序在每次检查 timer_expired 时都获取最新值。

3. 多线程应用中共享的 volatile 变量

在多线程程序中,两个线程可能会访问和修改同一个共享变量。

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

volatile bool stop_flag = false; // 用于控制线程结束的标志

void *worker_thread(void *arg) {
    while (!stop_flag) {
        // 执行一些任务
    }
    printf("Worker thread stopping.\n");
    return NULL;
}

int main() {
    pthread_t thread;

    // 创建一个工作线程
    pthread_create(&thread, NULL, worker_thread, NULL);

    // 主线程执行一些任务
    printf("Main thread running.\n");
    sleep(1);

    // 设置停止标志
    stop_flag = true;

    // 等待工作线程结束
    pthread_join(thread, NULL);

    printf("Main thread exiting.\n");

    return 0;
}

在这个例子中,stop_flagvolatile 的,因为主线程和工作线程都会访问和修改它。声明为 volatile 可以防止编译器优化读取操作,确保每次访问都获取最新的值。


总结

  • volatile 关键字确保编译器不会对变量进行优化,每次访问都从内存中读取最新的值。
  • 适用于可能被程序外部事件(如硬件、中断、多线程)修改的变量,确保这些值在程序中始终是最新的。

编译器优化是编译器在将源代码转换为机器代码的过程中进行的一系列技术手段,旨在提高生成代码的执行效率、减小代码大小或改善运行时性能。优化的方式可以包括:

  1. 删除冗余代码:去除未使用或多余的代码段,减少最终生成的程序体积。
  2. 常量折叠:在编译时计算常量表达式的值,而不是在运行时计算。
  3. 循环优化:对循环结构进行改进,例如循环展开(减少循环次数)或将不变的计算移出循环。
  4. 变量寄存器分配:将频繁使用的变量存储在 CPU 寄存器中,而不是内存中,提高访问速度。

优化的影响

  • 性能提升:经过优化的代码通常运行更快。
  • 内存使用:优化可能导致更小的可执行文件,节省内存空间。
  • 代码可读性:有时优化可能使得生成的机器代码与源代码的逻辑关系不明显,这可能影响调试。

例子

假设有以下简单代码:

int main() {
    int a = 5;
    int b = 10;
    int c = a + b;
    return c;
}

经过优化后,编译器可能将其简化为:

int main() {
    return 15; // 直接返回常量值
}

这种优化显著提高了程序的执行效率,因为去除了不必要的计算。

需要注意

在某些情况下,例如使用 volatile 关键字时,编译器被告知不应对该变量进行优化,确保每次访问都是最新值。这是因为编译器可能会假设某些变量在某些上下文中不会变化,因此在对这些变量的访问上可能进行优化,从而影响程序的正确性。


http://www.kler.cn/a/376536.html

相关文章:

  • Vagrant使用教程:创建CentOS 8虚拟机
  • qt QPicture详解
  • SpringBoot中扩展Druid的过滤器实现完整的SQL打印
  • (56)MATLAB分析码间串扰信道的传递函数与频率响应
  • 智能指针(内存泄漏问题)
  • 使用linuxdeployqt打包Qt程序问题及解决方法
  • Vue2——单页应用程序路由的使用
  • SpringBoot实现国密通信
  • 基于MATLAB驾驶行为的疲劳实时检测研究
  • android数组控件Textview
  • sublime Text中设置编码为GBK
  • 电子时钟--html+css+js实现
  • 【热门主题】000011 React前沿:构建高效与灵动的现代Web应用
  • 分布式事务-SpringBoot集成Seata
  • Mybatis学习笔记(二)
  • python项目实战——多协程下载美女图片
  • 【uniapp3】分享一个自己写的h5日历组件
  • CSS例子: 横向排列的格子
  • 安装fpm,解决*.deb=> *.rpm
  • 六、元素应用CSS的习题
  • 网络编程 UDP编程 Linux环境 C语言实现
  • 在AdaBoost中每轮训练后,为什么错误分类的样本权重会增大e^2αt倍
  • 使用socket库创建简单的客户端和服务器
  • 快速入门kotlin编程(精简但全面版)
  • 树莓派基本设置--8.播放音频和视频
  • 服务器数据恢复—DELL EqualLogic PS6100系列存储简介及如何收集故障信息?