C语言volatile解析

在百度百科中volatile关键字是这样解释的:

volatile是一个特征修饰符(type specifier)。volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。一般在以下场景中使用:

  • 并行设备的硬件寄存器(如:状态寄存器)
  • 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
  • 多线程应用中被几个任务共享的变量

仅看上述内容总感觉让人不够直观,与其绞尽脑汁去细品,不如直接写段代码,看看无volatile和带volatile的变量在编译后生成的汇编代码有哪些区别。

因为笔者相对熟悉arm平台,所以选用了armcc编译器,版本为:ARM Compiler 5.06 update 6 (build 750),优化等级为O3,操作系统平台为Windows 10.0.18362,具体编译命令如下:

armcc.exe -O3 -S D:\volatile测试\volatile.c -o D:\volatile测试\volatile.s

无volatile代码如下:

int a=1;
int b=2;
int c;
int main()
{
    b=a;
    c=b;
    return 0;
}

使用volatile修饰变量b,修改后的代码(带volatile)如下:

int a=1;
volatile int b=2;
int c;
int main()
{
    b=a;
    c=b;
    return 0;
}

无volatile代码生成的关键汇编(部分)如下:

main PROC
        LDR      r0,|L0.24|
        LDR      r1,[r0,#0]  ; 将变量a载入到寄存器r1
        STR      r1,[r0,#4]  ; 将寄存器r1存储至变量b
        STR      r1,[r0,#8]  ; 将寄存器r1存储至变量c
        MOV      r0,#0
        BX       lr
        ENDP

而带volatile代码生成的汇编如下:

main PROC
        LDR      r0,|L0.28|
        LDR      r1,[r0,#0]  ; 将变量a载入寄存器r1
        STR      r1,[r0,#4]  ; 将寄存器r1存储至变量b
        LDR      r1,[r0,#4]  ; 将变量b载入寄存器r1
        STR      r1,[r0,#8]  ; 将寄存器r1存储至变量c
        MOV      r0,#0
        BX       lr
        ENDP

对此,我们可以清楚的发现:在未使用volatile的汇编代码中,当变量b赋值给变量c时,直接将寄存器r1更新至变量c;而在使用volatile修饰变量b以后,同样变量b赋值给变量c时,先把变量b的值更新至寄存器r1(强制更新,红底色代码),再把寄存器r1更新至变量c。使用volatile修饰的变量会被编译器标记为易改变的变量,编译器在处理该变量赋值操作时会添加同步该变量(内存)至寄存器的操作。

posted @ 2020-02-19 22:58  低调的LEO  阅读(2013)  评论(0编辑  收藏  举报