有网友评论了我前面的中断实验的c代码,说全局变量需加上volatile关键字。平时写Java程序也没有用到这个,只知道这个用于多线程,但也不能完全解决多线程并发修改共享变量的问题。这几天我都在思考这个问题,向干了多年嵌入式开发的同学请教,还问了下韦东山老师,然后下文就是我的这个问题的理解。
测试两种情况 ,分别给全局变量INTNUM加上volatile和不加volatile,然后在AtmelStudio里调试中断函数里INTNUM++这行代码,看两者的汇编代码。下图是不加volatile的情况:
从上面汇编代码可以看出,编译器优化在重复使用变量INTNUM时,暂时不保存进内存,第二次直接用寄存器的,但最终还是保存进内存了。分两种情况,进入if操作之后保存,没进if就直接保存了。为什么编译器要这么做,网上说是优化,那么优化在何出?我用一个加了volatile的与上图对比:
对比可以知道,在用了volatile且进了if的情况,就要执行两组保存命令。而不用volatile且进了if的情况就只执行一组保存命令。这就是优化点。
这时我又了产生了两个比较傻的问题:
1.为什么汇编老要操作寄存器,不直接操作内存呢?
这个可以查下知乎,我找到的答案是CPU(ALU)只能操作寄存器,这是从机械(距离)和执行速度两角度决定的。
2.LDS指令上来就把寄存器R24的值给覆盖了,不怕这里面的值有用啊?
我目前的领悟是这样,正常的main函数里,都是取数据、保存数据成对出现的,寄存器相当于中间的临时变量。用过之后就可以复用,不会有什么问题。如果是中断程序里,生成的汇编代码就有保存现场压栈(PUSH),出栈(POP)操作,压栈时会保存寄存器的值,不管中断程序里怎么变,出中断程序时还能恢复原来的值,所以也没有问题。
记录下avr c语言里使用汇编的方法。在汇编这个级别,如果改变寄存器,那么用不用valotile,数据都会被搞乱。如下图:
加上这句汇编,我之前的计数中断会产生什么效果?哈哈,就是1次加2呗。
另外还有一个问题记录下,c语言可以定义一个全局变量,再定义一个局部变量与全局变量重名。在atmelStudio7中debug程序会在局部变量的后一行发生错误(不再执行下一行)。这个代码是能编译成功的,编译成功后我再下载到atmel328p上,果然运行到那行重复的局部变量,数码管的数字就不动了。