了解C语言中的volatile

(根据参考文献翻译整理而成)

volatile出现的背景

    我们知道编译器将C代码转换成机器代码,这样就可以在没有实际源代码的情况下运行可执行文件。在将源代码转换为机器代码时,编译器通常会尝试优化输出,以便最终执行较少的机器代码。其中一个优化是删除从编译器视角看来无用的、用于访问变量的机器代码。假设我们有以下代码:

uint32 status = 0; 
  
while (status == 0) 
{
    
     
  /*Let us assume that status isn't being changed  
  in this while loop or may be in our whole program*/
  
  /*So long as status (which could be reflecting  
  status of some IO port) is ZERO, do something*/
} 

    编译器认为在while循环中,status变量没有变化。因此,在循环的每次迭代之后,不需要一次又一次地访问status变量。因此编译器会将这个循环转换成一个无限循环,即while(1),这样就不需要机器代码来读取status。但是,这种优化有时会产生错误。例如,某个外围设备IO端口被内存映射到status变量,当外围设备发生了IO操作,status变量会改变。所以实际上,我们希望在每次循环迭代之后编译器能够访问status变量,即使该变量没被程序修改。
    有人可以说可以关闭此类程序的所有编译器优化,以免遇到这种情况。由于多种原因,这不是一个好的选择,因为:

  1. 每个编译器实现都是不同的,所以关闭编译器优化不是一个可移植的解决方案;
  2. 仅仅为了一个变量,不至于放弃编译器对程序其他部分所做的优化。
  3. 关闭所有优化,底层程序无法按预期运行。

    这时,需要使用volatile。volatile向编译器说明status为特殊变量,不允许对该变量进行优化。这样,定义变量如下:

volatile uint32 status = 0;

    通常,volatile与如下指针一起使用:

volatile uint32 * statusPtr = 0xF1230000

    这里,statusPtr指向内存地址(例如,该地址用于某个IO端口),外围设备可以随时修改内存地址的内容。请注意,内部程序无法控制,也不知道内存中的内容什么时候被修改。因此,我们将其设置为“ volatile ”,编译器不会对statusPtr指向的volatile变量执行优化。
    可以推测,程序中包含太多“volatile”变量也会导致越来越少的编译器优化。
    在本文中,我们希望您采用“volatile 变量 = 不要对该变量进行编译器优化”的概念。

示例

    在下面示例中,编译器不会进行任何优化,并且会更改const对象的值。

/* Compile code without optimization option */
#include <stdio.h> 
int main(void) 
{
    
     
    const int local = 10; 
    int *ptr = (int*) &local; 
  
    printf("Initial value of local : %d \n", local); 
  
    *ptr = 100; 
  
    printf("Modified value of local: %d \n", local); 
  
    return 0; 
} 

    当我们使用gcc的“ -save-temps”选项编译代码时,它将生成3个输出文件
    1)预处理代码(具有.i扩展名)
    2)汇编代码(具有.s扩展名)
    3)目标代码(具有.o选项)。
    我们在不优化的情况下编译代码,汇编代码会更大。

  [narendra@ubuntu]$ gcc volatile.c -o volatile –save-temps
  [narendra@ubuntu]$ ./volatile
  Initial value of local : 10
  Modified value of local: 100
  [narendra@ubuntu]$ ls -l volatile.s
  -rw-r–r– 1 narendra narendra 731 2016-11-19 16:19 volatile.s
  [narendra@ubuntu]$

    让我们用优化选项(即-O选项)编译相同的代码。GCC编译器进行了优化,并忽略了试图更改const对象值的指令。因此,const对象的值保持不变。

  [narendra@ubuntu]$ gcc -O3 volatile.c -o volatile –save-temps
  [narendra@ubuntu]$ ./volatile
  Initial value of local : 10
  Modified value of local: 10
  [narendra@ubuntu]$ ls -l volatile.s
  -rw-r–r– 1 narendra narendra 626 2016-11-19 16:21 volatile.s

    同时,因为编译器优化,汇编代码会减小。
    最后,让我们将const对象声明为volatile并使用优化选项编译代码。尽管我们使用优化选项编译代码,const对象的值应该发生变化,但因为变量被声明为volatile,这意味着不进行任何优化。

/* Compile code with optimization option */
#include <stdio.h> 
  
int main(void) 
{
    
     
    const volatile int local = 10; 
    int *ptr = (int*) &local; 
  
    printf("Initial value of local : %d \n", local); 
  
    *ptr = 100; 
  
    printf("Modified value of local: %d \n", local); 
  
    return 0; 
} 

    输出:

  [narendra@ubuntu]$ gcc -O3 volatile.c -o volatile –save-temp
  [narendra@ubuntu]$ ./volatile
  Initial value of local : 10
  Modified value of local: 100
  [narendra@ubuntu]$ ls -l volatile.s
  -rw-r–r– 1 narendra narendra 711 2016-11-19 16:22 volatile.s
  [narendra@ubuntu]$

    注意:上面的代码特定于编译器,可能不适用于所有编译器。

参考文档

[1]GeeksforGeeks.Understanding “volatile” qualifier in C | Set 1 (Introduction)[EB/OL].https://www.geeksforgeeks.org/understanding-volatile-qualifier-c-set-1-introduction/,2017-06-02.
[2]GeeksforGeeks.Understanding “volatile” qualifier in C | Set 2 (Examples)[EB/OL].https://www.geeksforgeeks.org/understanding-volatile-qualifier-in-c/,2020-04-26.

猜你喜欢

转载自blog.csdn.net/zsx0728/article/details/113315049