如果我们在volatile
每次更新新值时都
声明一个变量,如果我们声明一个变量,const
则该变量的值将保持不变
那么,如上所述const volatile int temp;
声明变量temp
有什么用?
如果声明为,会发生什么const int temp
?
Answers:
const volatile
代码不允许将标记为的对象进行更改(由于const
限定符会引起错误)-至少通过该特定名称/指针。
volatile
限定符的一部分意味着编译器无法优化或重新排序对对象的访问。
在嵌入式系统中,这通常用于访问硬件寄存器,这些寄存器可以被硬件读取和更新,但是写入时没有意义(或者可能是写入错误)。
一个示例可能是串行端口的状态寄存器。各个位将指示字符是否正在等待读取或发送寄存器是否准备好接受新字符(即-它为空)。每次读取此状态寄存器都可能产生不同的值,具体取决于串行端口硬件中发生的其他情况。
写入状态寄存器没有任何意义(取决于特定的硬件规格),但是您需要确保每次读取寄存器都会导致对硬件的实际读取-使用先前读取的缓存值将不会告诉您有关硬件状态的变化。
一个简单的例子:
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
,则可能会发生几个问题:
*recv_reg
该循环已更改,因此没有理由在进入循环之前无法对其进行读取。在volatile
预选赛确保这些优化不是由编译器执行。
volatile
会告诉编译器不要优化与该变量相关的代码,通常是在我们知道可以从“外部”更改该变量时(例如,由另一个线程更改)。const
会告诉编译器禁止程序修改变量的值。const volatile
这是非常特殊的事情,您可能会发现生命(tm)中使用了0次。可以预期,这意味着程序无法修改变量的值,但是可以从外部修改该值,因此不会对该变量进行优化。volatile
变量通常是当您开始与硬件而不是其他线程打交道时发生的事情。我见过的地方const volatile
是内存映射状态寄存器之类的东西。
并不是因为变量是const,所以它在两个序列点之间可能没有变化。
不变性是您做出的不更改值的承诺,而不是值不会更改。
const
数据不是“恒定的”。
我需要在嵌入式应用程序中使用它,其中一些配置变量位于闪存区域中,可以由引导加载程序进行更新。这些配置变量在运行时是“恒定的”,但是如果没有volatile限定符,编译器将优化这样的内容。
cantx.id = 0x10<<24 | CANID<<12 | 0;
...通过预先计算常数值并使用立即汇编指令,或从附近位置加载常数,从而将忽略对配置闪存区域中原始CANID值的任何更新。CANID必须是常量易失的。
在C中,const和volatile是类型限定符,并且这两个是独立的。
基本上,const表示该值不能由程序修改。
volatile表示该值可能会突然更改(可能从程序外部)。
实际上,C标准提到了一个有效声明的示例,该声明是const和volatile的。这个例子是
“ extern const volatile int real_time_clock;”
其中real_time_clock可以由硬件修改,但不能分配,递增或递减。
因此,我们应该已经分别处理const和volatile了。此外,这些类型限定符也适用于struct,union,enum和typedef。
const
表示该变量不能用C代码修改,不是不能更改。这意味着没有指令可以写入变量,但其值可能仍会更改。
volatile
意味着该变量可以随时更改,因此不可以使用任何缓存的值;每次对变量的访问都必须执行到其内存地址。
由于该问题被标记为“嵌入”并且假设 temp
是用户声明的变量,而不是与硬件相关的寄存器(因为它们通常在单独的.h文件中处理),请考虑:
具有易失性读写数据存储器(RAM)和非易失性只读数据存储器的嵌入式处理器,例如von-Neumann体系结构中的FLASH存储器,其中数据和程序空间共享公共数据和地址总线。
如果声明const temp
具有值(至少等于0),则编译器会将变量分配给FLASH空间中的地址,因为即使将其分配给RAM地址,它仍需要FLASH存储器来存储初始值因为所有操作都是只读的,所以使RAM地址浪费空间。
结果:
int temp;
是存储在RAM中的变量,在启动(cstart)时初始化为0,可以使用缓存的值。
const int temp;
是存储在(只读)FLASH中的变量,在编译时初始化为0,可以使用缓存的值。
volatile int temp;
是存储在RAM中的变量,在启动(cstart)时初始化为0,将不使用缓存的值。
const volatile int temp;
是存储在(read-ony)FLASH中的变量,在编译时初始化为0,将不使用缓存的值
这是有用的部分:
如今,大多数嵌入式处理器都具有通过特殊功能模块对其只读非易失性存储器进行更改的能力,在这种情况下const int temp
可以在运行时更改,而不必直接更改。换句话说,函数可以修改temp
存储位置的值。
一个实际的例子是使用temp
设备序列号。嵌入式处理器第一次运行时,temp
将等于0(或声明的值),并且函数可以使用此事实在生产期间运行测试,如果成功,则要求分配一个序列号并修改以下值:temp
通过以下方式特殊功能。为此,某些处理器具有带有OTP(一次性可编程)存储器的特殊地址范围。
但是不同之处在于:
如果const int temp
是可修改的ID而不是一次性可编程的序列号并且未声明volatile
,则可能在下一次启动之前使用缓存的值,这意味着新的ID可能在下一次重新启动之前才有效,甚至更糟的是,某些功能可能会使用新值,而其他可能会使用旧的缓存值,直到重新启动。如果const int temp
声明voltaile
为IS ,则ID更改将立即生效。
本文讨论了要将const和volatile限定符组合在一起的方案。
http://embeddedgurus.com/barr-code/2012/01/combining-cs-volatile-and-const-keywords/
const volatile int temp;
在块范围内使用(即,在内{ }
),那里没有用。