C语言关键字_volatile

本文详细介绍了volatile关键字的用法及注意事项,包括其如何阻止编译器优化、确保变量的可见性和一致性,特别是在多线程环境中的作用。同时,通过实例说明了volatile变量的应用场景,并讨论了相关问题。

volatile关键字

用法:1、告诉编译器不做任何优化
      2、表示用volatile定义的变量会在程序外被改变,每次都必须要从内存中读取,而不能把它放在cache或寄存器中重复使用

volatile是告诉编译器对它所修饰的对象别执行优化。volatile在进行多线程编程时要注意,而在单线程中那个就是只能起到限制编译器优化的作用。如果一个变量被volatile修饰,编译器将不会把它保存到寄存器中,而是每一次都去访问内存中实际保存给变量的位置的值。这样就避免了没有volatile修饰的变量在多线程的读写中所长生的由于编译器优化所导致的问题。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量有别的程序更新了的话,讲出现不一致的现象。
volatile int flag=1;

while(flag)
{
... ...
}
while的编译条件就不会被编译器当作无条件真。而是每一次都去内存中看一下这个flag的值是不是为真。

volatile变量的例子
1)并行设备的硬件寄存器(状态寄存器);
2)一个中断服务寄存器中会访问到的非自动变量
3)多线程应用中被几个任务共享的变量

volatile的几个问题
1)一个参数既可以是const还可以是volatile吗?为什么?
2)一个指针可以是volatile吗?为什么?
3)下面的函数有什么错误:
int square(volatile int *ptr)
{
    return *ptr * *ptr;
}

答案:1)是的,一个例子是只读状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去改变它。
2)是的。尽管这病不很常见。一个例子是打好一个中断服务自耦程序修改一个指向一个volatile型参数,编译器讲产生类似下面的代码:
int square(volatile int *ptr)
{
    int a,b;
    a = *ptr;
    b = *ptr;
    return a * b;
}
由于*ptr的值可能被意想不到地改变,因此a和b可能是不同的。结果,这段到嘛可能不是你所期望的平方值!按正确的代码如下
long square(volatile int *ptr)
{
    int a;
    a = *ptr;
    return a * a;
}

### 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` 并不足以完全规避这些问题。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值