12. C语言关键字 volatile

本文介绍了C语言中volatile关键字的基本概念及其应用场景。volatile用于确保变量的内存可见性,避免编译器优化导致的不可预期行为,尤其是在多线程或多CPU编程环境中。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  volatile 是C语言中的一个关键字,他的出场率很低,但有的时候,它的作用也是至关重要的。
  在介绍这个关键字之前,我们先看一个例子。

    include <stdio.h>
    int main() {
        const int n = 10;
        int *p = (int *)&n;
        *p = 20;
        printf("%d\n", n);
        return 0;
    }

  大家猜猜看这个程序的结果是什么。

  10?

  错了,他的结果是还是20。

运行结果

  但是得到这个结果的前提是开启 gcc 编译的 -O2 选项,这个选项是开启二级优化的意思,即编译器在编译期间,可能对代码进⾏优化。

  当编译器看到这⾥的num被const修饰,从语义上讲这⾥的num是不期望被改变(不改变)的,那优化的时候就可以把num的值存放到寄存器(以提⾼访问的效率)中。以后只要使⽤
num的地⽅都去寄存器中取,那即使num对应的内存中的值发⽣变化,寄存器也是感知不到
的。所以造成输出10的结果。

  寄存器变量有一个特点,内就是内存的不可见性。因为寄存器和内存是两片物理空间,所以存储在寄存器中的变量是无法通过指向内存空间的指针表示的。所以 *p = 20 是没有任何效果的。

  我们如果对上面的程序做了如下修改

    include <stdio.h>
    int main() {
        // 使用关键字 volatile 修饰变量
        volatile const int n = 10;
        int *p = (int *)&n;
        *p = 20;
        printf("%d\n", n);
        return 0;
    }

  运行结果:

运行结果

  这就是关键字 volatile 最直观的作用表现了。

volatile

  • 作用:

  编译时不优化,执⾏时不缓存,每次需从内存中读出(保证内存的可⻅性)。
防止因为编译器的自动优化等动作,使某些变量放到了寄存器中,寄存器中的变量不能寻址,所以使用volatile关键字可以强制将变量存放在内存中。

  • 应用场景:

  ⽤于多线程或多CPU编程。

### C语言中 `volatile` 关键字的作用和使用场景 #### 1. **`volatile` 的基本定义** 在C语言中,`volatile` 是一种类型限定符,用于告诉编译器该变量可能会被程序外部的因素改变,因此每次访问这个变量时都必须直接从内存读取,而不是依赖于寄存器中的缓存副本[^2]。 #### 2. **`volatile` 的主要作用** - 防止编译器优化:当声明一个变量为 `volatile` 后,编译器不会对该变量进行任何假设性的优化操作。这意味着即使看起来可以省略某些读写操作,编译器也会严格按照源码执行这些操作。 - 确保数据一致性:由于硬件或其他线程可能随时更改此变量的值,通过标记为 `volatile` 可以保证每次使用的都是最新的实际存储值。 #### 3. **典型使用场景** ##### 场景一:中断处理 在一个嵌入式系统中,如果某个全局变量可以在中断服务例程(ISR) 和主线程之间共享,则应该将其设置成 `volatile` 类型。这是因为ISR会异步地更新这个变量,而主线程也需要及时感知到这种变化。 ```c volatile uint8_t interrupt_flag = 0; void ISR() { interrupt_flag = 1; } int main() { while (interrupt_flag == 0); // Wait until interrupted return 0; } ``` ##### 场景二:多线程环境下的通信标志位 当多个线程共同维护同一个状态指示量或者信号灯的时候,为了防止因指令重排而导致逻辑错误,应当采用 `volatile` 来修饰此类变量。 ```c #include <pthread.h> volatile int done = 0; void* thread_func(void *arg){ sleep(5); done = 1; pthread_exit(NULL); } int main(){ pthread_t tid; pthread_create(&tid, NULL, thread_func, NULL); while(!done){} /* Busy waiting */ printf("Thread finished.\n"); pthread_join(tid, NULL); return 0; } ``` ##### 场景三:硬件寄存器映射 对于那些直接对应物理设备地址的空间位置来说(比如I/O端口),它们的状态往往是由外界条件决定而非软件控制流程所影响,所以也需用上 `volatile` 定义相应的结构体成员或指针指向这样的区域[^1]。 ```c struct HardwareRegister{ volatile unsigned char control_register; }; #define HW_BASE_ADDR ((struct HardwareRegister *)0xFFFF0000) void set_control_bit(){ HW_BASE_ADDR->control_register |= (1 << 3); } ``` #### 注意事项 虽然 `volatile` 能够解决很多同步问题,但它并不能替代互斥锁来实现复杂的并发保护机制;另外,在现代CPU架构下还可能存在其他层面的乱序现象,仅靠 `volatile` 并不足以完全规避这些问题。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值