前言
volatile ˈ/välədl/ 在嵌入式工作中经常会使用到,因此掌握volatile的使用非常重要。
意义
该关键字的意义就是表示定义的变量值随时都会改变,必须从变量的地址处读取值,所以只有这个变量在使用过程中可能被改变(比如中断程序),就需要用这个关键字说明。
1)volatile,关键字volatile 确保本条指令不会因C 编译器的优化而被省略,且要求每次直接读值。以下边代码举例:
int *ip =...; //设备地址
*ip = 1; //第一个指令
*ip = 2; //第二个指令
// 以上程序compiler可能做优化而成:
int *ip = ...;
*ip = 2;
// 结果第一个指令丢失。如果用volatile, compiler就不允许做任何的优化,从而保证程序的原意:
volatile int *ip = ...;
*ip = 1;
*ip = 2;
2)共享的内存地址,多个程序都对它操作的时候。你的程序并不知道,这个内存何时被改变了。如果不加这个voliatile修饰,程序是利用catch当中的数据,那个可能是过时的了,加了 voliatile,就在需要用的时候,程序重新去那个地址去提取,保证是最新的。
使用场景
1.操作系统下多任务环境下任务之间共享标志。
2. 硬件寄存器。
3. 中断和主循环都会用到的全局变量。举例来讲:
unsigned char cnt;
void TIM1_UP_IRQHandler(void)
{
if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
{
cnt--;
TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
}
}
int main(void)
{
cnt = 10;
while (cnt) // 这些在编译器中将会形成一个死循环,而不是预计的等待一段时间。
{
....
}
}
volatile和const
volatile和const一起用,没有冲突,const针对本程序而言,保证该值不变;volatile应付其他情况(如多线程,中断),避免优化后出现错误
volatile和指针
举例来讲:
uchar * volatile reg
行代码里volatile修饰的是reg这个变量。所以这里实际上是定义了一个uchar类型的指针,并且这个指针变量本身是volatile 的。但是指针所指的内容并不是volatile的!在实际使用的时候,编译器对代码中指针变量reg本身的操作不会进行优化,但是对reg所指的内容却会作为non-volatile内容处理。通常这种写法一般用在对共享指针的声明上,即这个指针变量有可能会被中断等函数修改。将其定义为volatile以后,编译器每次取指针变量的值的时候都会从内存中载入,这样即使这个变量已经被别的程序修改了,当前函数用的时候也能得到修改后的值(否则通常只在函数开始取一次放在寄存器里,以后就一直使用寄存器内的副本)。
区别代码:
volatile uchar *reg;
这行代码里volatile修饰的是指针所指的内容。所以这里定义了一个uchar类型的指针,并且这个指针指向的是一个volatile的对象。但是指针变量本身并不是volatile的。如果对指针变量reg本身进行计算或者赋值等操作,是可能会被编译器优化的。但是对reg所指向的内容 reg的引用却禁止编译器优化。因为这个指针所指的是一个volatile的对象,所以编译器必须保证对reg的操作都不被优化。通常在驱动程序的开发中,对硬件寄存器指针的定义,都应该采用这种形式。