了解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.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言volatile关键字是用来声明一个变量为“易变”的。这意味着该变量的值可以在程序执行期间随时发生变化,可能由于硬件或者其他并行执行的线程的影响。使用volatile关键字可以告诉编译器不要对该变量进行优化,以确保每次访问该变量时都从内存读取最新的值。这对于需要频繁读写硬件寄存器或者在多线程环境下共享的变量非常有用。 参考资料: C语言深度解析专栏—C语言关键字详解第五篇 C语言再学习 -- 关键字volatile_聚优致成的博客-CSDN博客 更多关键字在下面博客链接 C语言关键字详解(一)auto、register关键字 C语言关键字详解(二)带你全面了解 static C语言关键字详解(三)数据类型与sizeof关键字 C语言关键字详解(四)带你全面了解 const 关键字<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [C语言关键字详解(五)带你全面了解 volatile 关键字](https://blog.csdn.net/m0_62391199/article/details/123746218)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [C语言volatile 关键字](https://blog.csdn.net/qq_58264156/article/details/127416196)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值