volatile const int i;缓存
(1)本程序段中不能对 i 作修改,任何修改都是非法的,或者至少是粗心,编译器应该报错,防止这种粗心;
(2)另外一个程序段则彻底有可能修改,所以编译器最好不要作太激进的优化。多线程
“const”的含义:“请作为常量使用”,而并不是“放心吧,那确定是个常量”。
“volatile”的含义:“请不要作没谱的优化,这个值可能变掉的”,而并不是“你能够修改这个值”。
所以,它们原本就不是矛盾的。函数
const:优化
eg.1:spa
const意味着“只读”,只读表示编译器不容许代码修改变量,或者说这个变量在此处不能被修改,但并不表示这个变量在其它地方不可以被修改(不能被修改岂不就成了常量?)线程
#include <stdio.h>
int main(void) {
int i = 5;
const int* p = &i;
*p = 6; // 不能够
i = 7; // 彻底能够,并且那个“const”的“*p”也跟着变成了7。
printf("%d\n", *p);
return 0;
}
设计
eg.2:指针
void funcA(const char* str) {
...
}
在上面的程序中,str所指向的内存区域就是只读的,但这个只读性只在函数funcA内部,
出了funcA,这块内存彻底有多是可以被修改的。
void funcB(void) {
char name[] = "Jim King";
funcA(name);
}对象
以上两例足够说明,用const修饰表达了咱们本身的代码不会改变所修饰的对象的值,可是别的代码有可能改变此对象的值。内存
volatile:
volatile表示禁止优化。由于编译器会认为若是代码没有改变变量,那么这个变量就不会改变,所以编译器会用寄存器把该变量缓存起来,每次须要读取变量值的时候,就从缓存中读取。若是使用volatile,就应该直接从初始的内存区去读数据。固然,每一次修改变量的值时,也是将新值写入到内存中,而不是只写到缓存中。这在大多数时候是正确的,可是在多线程或者中断的场合就不正确了。
eg.1:
中断程序。在下面的程序中,咱们将intr_func注册为中断函数,某个中断发生时触发这一函数:
unsigned char flag = 1;
int main(int argc, char **argv) {
reg_intr(XXX, intr_func);
while(flag) {
printf("hello\n");
}
return 0;
}
void intr_func(void) {
flag = 0;
}
当不加volatile时,编译器会直接将while条件中的flag换成1(即编译器作了优化),所以即便中断发生也没法结束循环;若是给flag加上volatile标识,编译器就不会作前述的优化,程序就依照设计的意图工做了。
const、volatile综合举例
eg.1:
嵌入式系统中比较常见的例子。不少嵌入式系统容许咱们访问外部寄存器,该寄存器的地址假设是0x0018,并假设该寄存器的最低位表示设备状态,1为忙碌,0为空闲。
#define GET_REG_VALUE(reg) (*reg) //get register value
const unsigned char* STATUS_REG = 0x0018; // status register
const unsigned char STATUS_BUSY = 0x01; //busy bit
while (GET_REG_VALUE(STATUS_REG) & STATUS_BUSY); // wait until free
// do something to operate the device
...
这段代码极可能会死循环。由于编译器会将地址0x0018的对象的值缓存起来,而后每次while的时候都从缓存中读取。虽然STATUS_REG的值是const unsigned char *,但这仅仅表示STATUS_REG寄存器是个只读寄存器,咱们不可以在代码中去写这个寄存器,但并不表示这个寄存器是不可以改变的。硬件完成了它的任务以后,就会把状态设置成空闲,所以该寄存器的最后一位在咱们循环的时候极可能已经发生了变化。所以在这样的地方,咱们要禁止编译器自做聪明的优化,方法以下:
const volatile unsigned char *STATUS_REG = 0x0018; /* status register */
总结此例:
从const角度看,说明了用const修饰表达了咱们本身的代码不会改变所修饰的对象的值,可是硬件有可能改变此对象的值。
从volatile角度看,因为以前变量const修饰了变量,致使了此变量“只读”。然而循环过程当中该变量又存在连续不变的状况,编译器就多管闲事的作了优化,致使后来寄存器发生变化也仍是死不要脸的进入循环,因此就加入volatile来告诫编译器不要乱优化。
1.咱们须要明白“volatile”的含义并不是是“non-const”,因此他们才能够放在一块儿。
2.在C/C++语言中,const没有反义词,若是一个变量没有const修饰,那它自己就是const的反义词,而并不是加上volatile才是const的反义词。
3.volatile标识一个变量意味着这个变量可能被非本程序的其余过程改变。若是一个变量不会被本程序改变,一般可能给它加上const,但若是该变量可能被其余程序改变而本程序又在检测这个变量的值,就须要给它加上volatile,因而变量就同时有volatile和const了。二者同时修饰一个对象的典型状况,是用于驱动中访问外部设备的只读寄存器。
4.对于非指针非引用的变量,const volatile同时修饰的意义确实不大(我的以为)。
const volatile int i=10;这行代码有没有问题?若是没有,那 i 究竟是什么属性?
回答一:没有问题,例如只读的状态寄存器。它是volatile,由于它可能被意想不到地改变;它是const,由于程序不该该试图去修改它。volatile和const并不矛盾,只是控制的范围不同,一个在程序自己以外,另外一个是程序自己。
回答二:没问题,const和volatile这两个类型限定符不矛盾。const表示(运行时)常量语义:被const修饰的对象在所在的做用域没法进行修改操做,编译器对于试图直接修改const对象的表达式会产生编译错误。volatile表示“易变的”,即在运行期对象可能在当前程序上下文的控制流之外被修改(例如多线程中被其它线程修改;对象所在的存储器可能被多个硬件设备随机修改等状况):被volatile修饰的对象,编译器不会对这个对象的操做进行优化。一个对象能够同时被const和volatile修饰,代表这个对象体现常量语义,但同时可能被当前对象所在程序上下文意外的状况修改。