C/C++ Volatile关键字的作用
Volatile关键字的作用,用简单的一句话来描述就是关闭编译器对于Volatile修饰的变量的优化。
参考资料【1】:
typedef struct { int command; int data; int isbusy; } MyHardwareGadget; void SendCommand (MyHardwareGadget * gadget, int command, int data) { // wait while the gadget is busy: while (gadget->isbusy) { // do nothing here. } // set data first: gadget->data = data; // writing the command starts the action: gadget->command = command; }
这里编译器的优化有多个方面:
1.编译器根据情况(比如检测到某个内存地址,只有读取没有写入)可能不会每次都会去内存中检测 gadget->isbusy的值,而是仅仅只读取一次,之后都是使用缓存(寄存器)中的数据;
2.指令可能会产生重排,比如gadget->data和gadget->command的赋值的顺序可能会发生变化。
假设gadget映射的是设备的IO地址,设备内部有可能会根据中断或其他条件来设置isbusy标志位,或者data、command的赋值是有依赖关系的,那么上述代码就可能存在问题了。
正确的使用方式是使用volatile进行修饰,禁止编译器的优化:
void SendCommand (volatile MyHardwareGadget * gadget, int command, int data) { // wait while the gadget is busy: while (gadget->isbusy) { // do nothing here. } // set data first: gadget->data = data; // writing the command starts the action: gadget->command = command; }
资料【2】中包含了一段话,正是上例想要说明的:
In the context of our discussion about “volatile“, we quote C language standard i.e. ISO/IEC 9899 C11 – clause 6.7.3 “An object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects.” “A volatile declaration may be used to describe an object corresponding to a memory-mapped input/output port or an object accessed by an asynchronously interrupting function. Actions on objects so declared shall not be ‘‘optimized out’’ by an implementation or reordered except as permitted by the rules for evaluating expressions.”
笔者按照教程【2】、【3】中的例子,在编译器上实际的验证了一下,编译环境为VS2015,注意这里都是采用的Release编译模式,Debug模式编译器并没有执行相关优化,看不出效果。
void testFuncA() { unsigned int status = 0; while (status==0) { printf("testFunc"); } }
反汇编代码可以看到这里并没有每次都去检测status的值,代码被优化成了while(true)的形式:
仅仅只用volatile 关键字修饰一下之后的效果:
void testFuncB() { volatile unsigned int status = 0; while (status==0) { printf("testFunc"); } }
反汇编代码可以看到这里每次都去检测status的值。
void testFuncC() { const int local = 10; int *ptr = (int*)&local; printf("Initial value of local : %d \n", local); *ptr = 100; printf("Modified value of local: %d \n", local); } void testFuncD() { volatile const int local = 10; int *ptr = (int*)&local; printf("Initial value of local : %d \n", local); *ptr = 100; printf("Modified value of local: %d \n", local); }
代码输出的结果如下:
Initial value of local : 10 Modified value of local: 10 Initial value of local : 10 Modified value of local: 100
参考资料:
【1】https://stackoverflow.com/questions/246127/why-is-volatile-needed-in-c
【2】https://www.geeksforgeeks.org/understanding-volatile-qualifier-c-set-1-introduction/
【3】https://www.geeksforgeeks.org/understanding-volatile-qualifier-in-c/