一段最简单的代码
int a = 10;
int foo()
{
int b = a;
// do something for delay
return b - a;
}
编译命令:gcc -c -O3 a.c -o a.o (-O3参数开启优化), 反汇编的结果是这样的
0000000000000000 :
0: 31 c0 xor eax,eax
2: c3 ret
直接返回一个0(返回值在eax中)。如果是仅仅是上面的代码,直接返回0自然没有问题,但如果是多线程的环境中,或者是在中断上下文中,a有可能在别的地方被随时更改,那么b-a就不定是0了,直接返回0的结果就不对了。(这里用锁来保护a是不行的,因为锁是在运行时生效的,而代码是在优化编译时进行的)。
将a的定义改成这样的,其他不变
volatile int a = 10;
同样的命令进行编译,反汇编的结果是这样的
0000000000000000 :
0: 8b 05 00 00 00 00 mov eax,DWORD PTR [rip+0x0] # 6 <foo+0x6>
6: 8b 15 00 00 00 00 mov edx,DWORD PTR [rip+0x0] # c <foo+0xc>
c: 29 d0 sub eax,edx
e: c3 ret
解释一下这段代码就是:首先将a的值赋给eax,再将a的值赋给edx,最后返回eax-edx,即使在两次赋值的中间过程中a的值发生了变化,结果都是对的。
所以volatile关键字的作用就是告诉编译器,该变量是随时有可能变化的,不要对其进行优化,每次用到的时候都要从内存中读取。