自学内容网 自学内容网

【C语言】volatile 防止编译的时候被优化

volatile 易变的
volatile是 C 和 C++ 中的一个类型修饰符,用于指示编译器该变量可能在程序之外被更改,因此不应对其进行优化。这在涉及硬件寄存器、信号处理或多线程编程时非常有用。

如果你做过单片机开发,你肯定写过这样的代码:延时函数。就是通过循环延长CPU的时间,常见于流水灯、蜂鸣器这些对时间精度要求不高的地方,但是这样的代码却存在着很严重的缺陷,如果编译的过程中开启了优化,有些编译器会认为这几行代码是废话,什么事情也没干,在寄存器中会跳过这部分代码。

于是,在C语言中有了一个很重要的关键字volatile,它的作用是防止编译的时候被优化,在变量之前加上volatile,编译的时候即便开启最高等级的优化gcc test.c -o test -03,原代码中的编译将不再遭到优化处理。volatile在嵌入式开发中非常的重要而且常用,尤其是操作硬件或者多线程的场景。因为编译器在很多时候为了提升程序的运行效率,会跳过和优化掉一些没用的代码,或者直接从寄存器中或者缓存中读取某些变量的值,因为从寄存器或者缓存中取值速度要比内存快,如果这个变量是个共享数据,或者有可能被中断等程序修改,那么,我们读取到的数据就有可能跟内存中的数据不一致。如果在编译中发生这样的事,接下来程序执行的过程中就会导致一系列的问题。

请添加图片描述
请添加图片描述

int a = 10; // 在内存占用4个字节空间,该空间取名为a,cpu将10存入内存a空间中

int b = a;  // 在内存占用4个字节空间,该空间取名为b,cpu将内存a的值取出来,保存在cpu寄存器中(寄存器a)
            // 将寄存器a 赋值给 内存b空间 
int c = a;  // 在内存占用4个字节空间,该空间取名为c,将寄存器a 赋值给 内存c空间
int d = a;  // 在内存占用4个字节空间,该空间取名为d,将寄存器a 赋值给 内存d空间
int e = a;  // 在内存占用4个字节空间,该空间取名为e,将寄存器a 赋值给 内存e空间

// 假如 在int c = a的时候,不惊动cpu 去修改了内存中a的值,那么我们期望 d 和 e 的值 要和修改后a 的值 一致
// 但是 d 和 e 中依旧存入的是在 寄存器a 的值,造成 d 和 e 的值达不到我们的期望 

// 更改方式:(初始化 int a 更改为 volatile)
volatile int a = 10;  //防止CPU对a变量进行优化,从此以后只要用到a的值,就必须去内存中重新读取  —— 每次必须区内存中拿,不去CPU中拿

加上volatile就是告诉编译器这个变量是不稳定的,无论编译器怎么优化,每次都得从内存中去读取,

请添加图片描述

volatile的用途:

  • 硬件寄存器访问:
    • 直接访问:确保每次访问硬件寄存器时都使用 volatile,以避免编译器优化。因为访问硬件设备的寄存器时,寄存器的值可能会在程序之外改变。
volatile uint32_t *timer_register = (uint32_t *)0x40000000;
*timer_register = 0x01;
  • 信号处理/中断处理:
    • 标志变量:将中断服务程序(ISR)中使用的标志变量声明为 volatile。
    • 在信号处理程序中使用的变量,因为信号可以在任何时候中断程序执行。
volatile int interrupt_flag = 0;

void ISR_Handler() {
    interrupt_flag = 1;
}
  • 多线程和共享变量:
    • 共享状态:在多线程应用中,声明那些在不同线程间共享且不受锁保护的变量为 volatile。
    • 在多线程环境中,一个线程可能会修改另一个线程正在读取的变量。
volatile bool data_ready = false;

示例:

#include <stdint.h>
#include <stdbool.h>

volatile bool data_ready = false;
volatile uint32_t *adc_result = (uint32_t *)0x40001000;

void ADC_ISR() {
    *adc_result = // 读取ADC值
    data_ready = true;
}

int main() {
    while (1) {
        if (data_ready) {
            // 处理ADC结果
            data_ready = false;
        }
    }
    return 0;
}

通过正确使用 volatile,可以确保嵌入式系统中硬件和软件的正确交互。

简单的说,volatile 是禁止编译器对该变量的访问进行优化(如缓存、寄存器存储),每次访问该变量时,都会从内存中读取或写入。


使用的时候需要注意的事项:

  1. 仅用于必要情况:不要滥用volatile,只在有硬件交互或异步变化时使用。
  2. 结合同步机制:volatile不能替代互斥锁、信号量等同步机制,需要与其他同步机制(如互斥锁、原子操作)结合使用以确保多线程安全。
  3. 读写原子性:volatile不能保证读写操作的原子性,需要时使用原子操作或禁用中断。
  4. volatile 不能保证线程安全,它仅防止编译器优化。

以上。

我是一个十分热爱技术的程序员,希望这篇文章能够对您有帮助,也希望认识更多热爱程序开发的小伙伴。
感谢!


原文地址:https://blog.csdn.net/qq_39725309/article/details/143891715

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!