volatile 关键词的使用

在学校的时候就听强哥有讲过volatile的用法,只是当时颇不在意,直到自己遇到被优化,才发现这个点有必要记下来!

我是在使用keil(MDK-ARM)的时候发现这个问题的,当时需要延时,随便写了个for的延时函数,导致程序没有产生预定的结果,加了volatile之后,运行正确!

其实,这个关键词在keil的帮助文档里就可以找到,仔细拜读了一下,发现文档解释的还是相当清楚的,摘抄如下:

Compiler optimization and the volatile keyword

4.7 Compiler optimization and the volatile keyword

Higher optimization levels can reveal problems in some programs that are not apparent at lower optimization levels, for example, missingvolatile qualifiers.

This can manifest itself in a number of ways. Code might become stuck in a loop while polling hardware, multi-threaded code might exhibit strange behavior, or optimization might result in the removal of code that implements deliberate timing delays. In such cases, it is possible that some variables are required to be declared as volatile.
The declaration of a variable as volatile tells the compiler that the variable can be modified at any time externally to the implementation, for example, by the operating system, by another thread of execution such as an interrupt routine or signal handler, or by hardware. Because the value of a volatile-qualified variable can change at any time, the actual variable in memory must always be accessed whenever the variable is referenced in code. This means the compiler cannot perform optimizations on the variable, for example, caching its value in a register to avoid memory accesses. Similarly, when used in the context of implementing a sleep or timer delay, declaring a variable as volatile tells the compiler that a specific type of behavior is intended, and that such code must not be optimized in such a way that it removes the intended functionality.
In contrast, when a variable is not declared as volatile, the compiler can assume its value cannot be modified in unexpected ways. Therefore, the compiler can perform optimizations on the variable.
The use of the volatile keyword is illustrated in the two sample routines of the following table. Both of these routines loop reading a buffer until a status flag buffer_full is set to true. The state of buffer_full can change asynchronously with program flow.
The two versions of the routine differ only in the way that buffer_full is declared. The first routine version is incorrect. Notice that the variable buffer_full is not qualified as volatile in this version. In contrast, the second version of the routine shows the same loop where buffer_full is correctly qualified as volatile.

Table 4-5 C code for nonvolatile and volatile buffer loops

Nonvolatile version of buffer loopVolatile version of buffer loop
int buffer_full;
int read_stream(void)
{
    int count = 0;
    while (!buffer_full)
    {
        count++;
    }
    return count;
}
volatile int buffer_full;
int read_stream(void)
{
    int count = 0;
    while (!buffer_full)
    {
        count++;
    }
    return count;
}
The following table shows the corresponding disassembly of the machine code produced by the compiler for each of the examples above, where the C code for each implementation has been compiled using the option -O2.

Table 4-6 Disassembly for nonvolatile andvolatile buffer loop

Nonvolatile version of buffer loopVolatile version of buffer loop
read_stream PROC
    LDR      r1, |L1.28|
    MOV      r0, #0
    LDR      r1, [r1, #0]
|L1.12|
    CMP      r1, #0
    ADDEQ    r0, r0, #1
    BEQ      |L1.12|      ; infinite loop
    BX       lr
    ENDP
|L1.28|
    DCD      ||.data||
    AREA ||.data||, DATA, ALIGN=2
buffer_full
    DCD      0x00000000
read_stream PROC
    LDR      r1, |L1.28|
    MOV      r0, #0
|L1.8|
    LDR      r2, [r1, #0];  ; buffer_full
    CMP      r2, #0
    ADDEQ    r0, r0, #1
    BEQ      |L1.8|
    BX       lr
    ENDP
|L1.28|
    DCD      ||.data||
    AREA ||.data||, DATA, ALIGN=2
buffer_full
    DCD      0x00000000
In the disassembly of the nonvolatile version of the buffer loop in the above table, the statement LDR r0, [r0, #0] loads the value of buffer_full into register r0 outside the loop labeled |L1.12|. Because buffer_full is not declared as volatile, the compiler assumes that its value cannot be modified outside the program. Having already read the value of buffer_full into r0, the compiler omits reloading the variable when optimizations are enabled, because its value cannot change. The result is the infinite loop labeled |L1.12|.
In contrast, in the disassembly of the volatile version of the buffer loop, the compiler assumes the value of buffer_full can change outside the program and performs no optimizations. Consequently, the value of buffer_full is loaded into register r0 inside the loop labeled |L1.8|. As a result, the loop |L1.8| is implemented correctly in assembly code.
To avoid optimization problems caused by changes to program state external to the implementation, you must declare variables as volatile whenever their values can change unexpectedly in ways unknown to the implementation.
In practice, you must declare a variable as volatile whenever you are:
  • Accessing memory-mapped peripherals.
  • Sharing global variables between multiple threads.
  • Accessing global variables in an interrupt routine or signal handler.
The compiler does not optimize the variables you have declared as volatile.

怎么样,解释得够详细吧。
这里提到三点:(1)访问内存映射的外围设备(2)多线程间共享全局变量(3)在中断或信号处理函数中
必须用volatile 来声明变量
并且要注意:这里是选用的-02的优化级别!这个在编译的时候要注意,如果选用不优化的时候,应该是可以不加关键词的,有待验证!


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值