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意外地改变。