volatile的本意是“易变的” 因为访问寄存器要比访问内存单元快的多,所以编译器一般都会作减少存取内存的优化,但有可能会读脏数据。当要求使用volatile声明变量值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。精确地说就是,遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问;如果不使用valatile,则编译器将对所声明的语句进行优化。
以前一直只知道volatile的文字解释,但是还是希望通过看到实际的汇编代码后加深理解,所以就做了如下实验。
1. 写了一个简单的C函数如下,用arm-linux-gcc编译(加优化-O1)后查看变量在加volatile与不加之后的区别。
//c源代码如下:
void C_test(int *c)
{
int a = 100;
a++;
(*c)++;
C_test_1(c, a);
a+=5;
}
c代码中int a变量在没有加volatile时,编译出来的汇编代码如下:
000080f4 <C_test>:
80f4: e52de004 push {lr} ; (str lr, [sp, #-4]!) //保存返回地址
80f8: e24dd00c sub sp, sp, #12
80fc: e3a02064 mov r2, #100 ; 0x64
8100: e58d2004 str r2, [sp, #4]
8104: e59d2004 ldr r2, [sp, #4]
8108: e2822001 add r2, r2, #1 //a++;a放在sp+4的栈地址中
810c: e58d2004 str r2, [sp, #4]
8110: e5902000 ldr r2, [r0]
8114: e2822001 add r2, r2, #1 //(*c)++;
8118: e5802000 str r2, [r0]
811c: e59d1004 ldr r1, [sp, #4]
8120: ebffffee bl 80e0 <C_test_1> //跳到子函数
8124: e59d3004 ldr r3, [sp, #4]
8128: e2833005 add r3, r3, #5 //a+=5;
812c: e58d3004 str r3, [sp, #4]
8130: e28dd00c add sp, sp, #12
8134: e49de004 pop {lr} ; (ldr lr, [sp], #4)
8138: e12fff1e bx lr
这上面可以看出,几乎和没有加编译器优化的汇编代码一样。
c代码中int a变量在加了volatile后,编译出来的汇编代码如下:
000080f4 <C_test>:
80f4: e92d4010 push {r4, lr}
80f8: e5902000 ldr r2, [r0]
80fc: e2822001 add r2, r2, #1 //(*c)++
8100: e5802000 str r2, [r0]
8104: e3a01065 mov r1, #101 ; 0x65 //a的自加过程直接就免了,而是直接传的立即数101给子函数
8108: ebfffff4 bl 80e0 <C_test_1> //跳到子函数
810c: e8bd4010 pop {r4, lr}
8110: e12fff1e bx lr
总结: 如果不想定义的变量被编译器优化....................