1.编译器优化,优化掉看起来重复无用的代码。
(1)并行设备的硬件寄存器(如状态寄存器)。当声明指向并行设备的硬件寄存器的指针时要用volatile修饰,因为寄存器可以随时被外设硬件修改。例如使用for循环对指向寄存器的指针循环读写的时候如果不加volatile修饰指针,编译器会优化,只执行最后一次循环的读写。
例如:假设要对一个设备进行初始化,此设备的某一个寄存器为0xff800000。
1 int *output = (unsigned int *)0xff800000;//定义一个IO端口; 2 int init(void) 3 { 4 int i; 5 for(i=0;i< 10;i++){ 6 *output = i; 7 } 8 }
经过编译器优化后,编译器认为前面循环半天都是废话,对最后的结果毫无影响,因为最终只是将output这个指针赋值为 9,所以编译器最后给你编译编译的代码结果相当于:
1 int init(void) 2 { 3 *output =9; 4 }
(2)使用空的for循环想实现延时时,如果循环变量不加volatile,则编译器会优化认为你的空for循环是没用的,从而不会实现延时的效果
(3)多线程应用中被几个任务共享的变量。比如如下程序:
1 XBYTE[2]=0x55; 2 XBYTE[2]=0x56; 3 XBYTE[2]=0x57; 4 XBYTE[2]=0x58;
对外部硬件而言,上述四条语句分别表示不同的操作,会产生四种不同的动作,但是编译器却会对上述四条语句进行优化,认为只有XBYTE[2]=0x58(即忽略前三条语句,只产生一条机器代码)。如果键入 volatile,编译器会逐一的进行编译并产生相应的机器代码(产生四条代码)。
2.编译器优化变量存储到高速缓存中
变量一般存放到内存中,而内存的读写速度较慢,因此编译器优化会将变量值写入一份到高速缓存中,每次读取变量值时都会从高速缓存中读数据。但是如果内存中的数据更新后,高速缓存中的数据没有及时更新,那么再在高速缓存中读取数据就会造成读取的数据和内存不一致的问题。因此此时变量要加上volatile关键字,此时读取变量值时直接在内存中读取。但这也会降低读写效率。
(1)一个中断程序中会修改某一变量,同时这个变量要在中断外使用,此时要在变量前面加上volatile关键字
1 typedef struct task{ 2 float task_interval;//任务执行间隔 3 volatile float task_counter_s;//计数器 4 void(*task_entry)(void); 5 }task_info_t; 6 7 void TIM2_IRQHandler(void) 8 { 9 if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET) 10 { 11 for(int i=0;i<tasknum;i++) 12 { 13 task_arr[i].task_counter_s++; 14 } 15 TIM_ClearITPendingBit(TIM2,TIM_IT_Update); 16 } 17 }