const和const之间的区别
如果我们每次更新新值都声明一个variables为volatile
如果我们声明一个variables为const
那么该variables的值将不会被改变
然后const volatile int temp;
上面声明variablestemp
的用法是什么?
如果我们声明为const int temp
会发生什么?
标记为const volatile
的对象不允许被代码改变(由于const
限定符会引发错误) – 至less通过那个特定的名字/指针。
限定符的volatile
部分意味着编译器无法优化或重新sorting对象的访问权限。
在embedded式系统中,这通常用于访问硬件寄存器,这些寄存器可以被硬件读取和更新,但写入没有意义(或者可能是写入错误)。
一个例子可能是串口的状态寄存器。 各种位将指示一个字符是否正在等待读取,或者如果发送寄存器准备好接受一个新的字符(即, – 它是空的)。 每次读取状态寄存器都可能导致不同的值,具体取决于串口硬件中发生了什么。
写入状态寄存器是没有意义的(取决于特定的硬件规格),但是您需要确保每次读取寄存器都会导致实际读取硬件 – 使用先前读取的caching值“告诉你硬件状态的变化。
一个简单的例子:
unsigned int const volatile *status_reg; // assume these are assigned to point to the unsigned char const volatile *recv_reg; // correct hardware addresses #define UART_CHAR_READY 0x00000001 int get_next_char() { while ((*status_reg & UART_CHAR_READY) == 0) { // do nothing but spin } return *recv_reg; }
如果这些指针没有标记为volatile
,则可能会出现一些问题:
- while循环testing可能只读取一次状态寄存器,因为编译器可能会认为无论它指向什么都不会改变(while循环testing或循环本身中没有任何东西可以改变它)。 如果在UART硬件中没有字符等待的情况下进入该function,即使收到一个字符,也可能会以无限循环结束。
- 接收寄存器的读取可以被编译器移到while循环之前 – 再次因为函数中没有任何东西指示
*recv_reg
被循环改变了,没有理由在进入循环之前不能读取它。
volatile
限定符确保这些优化不被编译器执行。
-
volatile
会告诉编译器不要优化与variables相关的代码,通常当我们知道它可以从“外部”(例如由另一个线程)改变时。 -
const
会告诉编译器禁止程序修改variables的值。 -
const volatile
是一个非常特殊的东西,你可能会看到在你的生活(tm)中正好使用0次。 正如所预料的那样,这意味着程序不能修改variables的值,但是可以从外部修改该值,因此不会对variables进行优化。
这不是因为variables是const的,它可能在两个序列点之间没有改变。
坚持是一个承诺,你不改变价值,而不是价值不会改变。
我需要在embedded式应用程序中使用它,其中一些configurationvariables位于可由引导加载程序更新的闪存区域中。 这些configurationvariables在运行时是“常量”的,但是如果没有volatile限定符,编译器会优化这样的东西…
cantx.id = 0x10<<24 | CANID<<12 | 0;
…通过预先计算常量值并使用立即汇编指令,或从附近的位置加载常量,以使configuration闪存区域中的原始CANID值的任何更新都将被忽略。 CANID必须是常量易失性的。
const
表示variables不能被c代码修改,不能修改。 这意味着没有指令可以写入variables,但其值可能仍然会改变。
volatile
表示variables可能随时更改,因此不会使用caching的值; 对variables的每次访问必须被执行到它的内存地址。
由于该问题被标记为“embedded”,并假定temp
是用户声明的variables,而不是硬件相关的寄存器(因为这些通常在单独的.h文件中处理),请考虑:
一种embedded式处理器,具有易失性读写数据存储器(RAM)和非易失性只读数据存储器,例如von-Neumann体系结构中的FLASH存储器,其中数据和程序空间共享公用数据和地址总线。
如果你声明const temp
有一个值(至less与0不同),编译器将把这个variables赋给FLASH空间中的一个地址,因为即使它被分配给一个RAM地址,它仍然需要FLASH存储器来存储variables的初始值,使得RAM地址浪费空间,因为所有操作都是只读的。
结果是:
int temp;
是存储在RAM中的variables,在启动时(cstart)初始化为0,可以使用caching的值。
const int temp;
是一个存储在(读写)FLASH中的variables,在编译时初始化为0,可以使用caching值。
volatile int temp;
是存储在RAM中的variables,在启动时(cstart)初始化为0,不会使用caching的值。
const volatile int temp;
是一个存储在(read-ony)FLASH中的variables,在编译时初始化为0,不会使用caching值
这里有用的部分:
现在大多数embedded式处理器都可以通过一个特殊的function模块来改变它们的只读非易失性存储器,在这种情况下const int temp
可以在运行时改变,不是直接的。 换言之,函数可以修改存储temp
的地址处的值。
一个实际的例子是将temp
用于设备序列号。 embedded式处理器第一次运行时, temp
将等于0(或声明的值),一个函数可以用这个事实在生产期间运行一个testing,如果sucessfull,请求分配一个序列号并修改temp
的值通过一个特殊的function。 一些处理器有一个特殊的地址范围,OTP(一次性可编程)存储器就是这样。
但是差异在于:
如果const int temp
是一个可修改的ID,而不是一次性可编程的序列号,并且没有被声明为volatile
,那么可能会使用一个caching的值,直到下一次启动,这意味着新的ID可能无效,直到下一次重新启动,甚至更糟糕的是,有些函数可能会使用新值,而其他函数可能会使用较旧的caching值,直到重新启动。 如果const int temp
声明为voltaile
,则ID更改将立即生效。
本文讨论您想要将常量和易失性限定符组合在一起的场景。
http://embeddedgurus.com/barr-code/2012/01/combining-cs-volatile-and-const-keywords/
在C中,const和volatile是types限定符,这两个是独立的。
基本上,const意味着该值不能被程序修改。
易变意味着价值突然变化(可能来自程序之外)。
实际上,C标准提到了一个有效声明的例子,它既是const也是volatile。 这个例子是
“extern const volatile int real_time_clock;”
real_time_clock可以被硬件修改,但不能被赋值,递增或递减。
所以我们应该已经分别处理const和volatile。 此外,这些types限定符也适用于struct,union,enum和typedef。
你可以一起使用const和volatile。 例如,如果0x30被假定为仅由外部条件改变的端口的值,则以下声明将防止意外副作用的可能性:
const volatile char *port = (const volatile char *)0x30;
当我们不希望程序改变它时,我们使用'const'关键字作为variables。 而当我们声明一个variables“const volatile”时,我们告诉程序不要改变它,编译器认为这个variables可以被来自外界的input意外地改变。