今天正好在群里跟朋友聊volatile的问题,测了一下gcc对volatile变量和正常变量的实现区别,顺便记一下。
备注:此volatile是在c语言在gcc下的测试结果和原理,和java里的volatile完全两个概念,不要混淆!不要混淆!不要混淆!
首先写一个很简单的demo:
1 2 3 4 5 6 7 8 9 10 11 12
| #include <stdio.h>
int a = 100;
void foo() { while (a > 10) {} }
int main() { foo(); return 0; }
|
重点就是看反编译后的foo函数的实现,循环判断a是否大于10,然后看a是普通变量和volatile修饰变量的差异:
普通变量的gcc编译后的汇编代码如下:
1 2 3 4 5 6 7 8
| 0000000000000620 <foo>: 620: 8b 05 ea 09 20 00 mov eax,DWORD PTR [rip+0x2009ea] # 将a的值从内存加载至寄存器 626: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0] 62d: 00 00 00 630: 83 f8 0a cmp eax,0xa # 将eax寄存器的值与0xa(10)做对比 633: 7f fb jg 630 <foo+0x10> # jg,如果大于目标值,跳转至630,继续对比eax和0xa 635: f3 c3 repz ret 637: 66 0f 1f 84 00 00 00 nop WORD PTR [rax+rax*1+0x0]
|
对变量a加上volatile修饰后的汇编代码如下:
1 2 3 4 5 6
| 0000000000000610 <foo>: 610: 8b 05 fa 09 20 00 mov eax,DWORD PTR [rip+0x2009fa] # 将a的值从内存加载至寄存器 616: 83 f8 0a cmp eax,0xa # 将eax寄存器的值与0xa(10)做对比 619: 7f f5 jg 610 <foo> # jg,如果大于目标值,跳转至610,重新将a的值从内存加载至eax 61b: f3 c3 repz ret 61d: 0f 1f 00 nop DWORD PTR [rax]
|
可以明显的看到,未加volatile的汇编是每次只跟eax寄存器中的值做对比,而加上volatile之后,是跳转至610,也就是会重新将a的值从内存加载至寄存器,保证每次a的值都是最新,从而保证了“可见性”