指针声明为常量以及易失性
在阅读时,我遇到了这种types的声明和下面的一行 –
const volatile char *p=(const volatile char *) 0x30;
p的值仅由外部条件改变
我没有得到什么外部条件。 而且这种宣言的实际用途是什么?
const
说你的程序stream不会修改p
指向的内容。 任何试图在解引用指针后修改该值将导致编译时错误:
*p = 'A'; // will not compile
请注意,这不是一个特别强的合同, 位置0x30
处的值仍然可以通过别名非常量指针( p
:
volatile char *q = 0x30; *q = 'A'; // will compile
打破这个合同的另一种方法是从p
:
*(volatile char *) p = 'A'; // will compile
但是, volatile
不排除可能由另一个线程,内核,asynchronous信号处理程序或可访问相同内存空间的外部设备引起的任何修改。 这样编译器就不能错误地假设p
指向的值不会改变,每次引用时都会从内存中加载它:
/* The character at 0x30 will be read on every iteration, even if the compiler has proven that the program itself doesn't modify the value at that address. */ while (*p) { ... }
如果编译器错误地优化了这样一个结构,它可能会发出指令,这些指令只能从内存中加载一次,然后保存在一个寄存器中。 寄存器本质上是一个独立的副本,对原始位置的任何更改都不会反映在那里,不用说,这可能会导致一些非常令人讨厌的错误。
考虑一个只读的硬件寄存器,例如你的网卡。
它可能会在程序的控制之外改变,所以编译器不允许在寄存器中caching它的值或者优化它。 因此, volatile
。
它是只读的,所以你不应该写信给它。 因此, const
。
首先,让我引用C11
标准的章节§6.7.3 types限定符的例子
一个对象声明
extern const volatile int real_time_clock;
可能会被硬件修改,但不能被分配,递增或递减。
另外,相关的脚注(134),
挥发性声明可以用于描述对应于存储器映射input/输出端口的对象或由asynchronous中断function访问的对象。 对于如此声明的对象的行为,除了expression式评估规则允许的情况外,不得通过实现或重新sorting来“优化”。
这意味着,variables的值可以由硬件修改(通过内存映射),但不能“编程”修改。
所以,这里的优点是双重的,
- 每当使用的值,将从内存中读取(不允许caching),给你最新的更新值(如果更新)。
- 该值不能被程序有意或无意地改变( 写下 )。
我们可以使用关于volatile关键字的文章Introduction :
只要variables值可能意外改变,就应该声明variables。 实际上,只有三种variables可以改变:
- 内存映射的外设寄存器
- 全局variables由中断服务程序修改
- multithreading应用程序中的全局variables
和:
embedded式系统包含真正的硬件,通常具有复杂的外设。 这些外设包含寄存器,其值可能会随程序stream程asynchronous变化。 作为一个非常简单的例子,考虑地址为0x1234的8位状态寄存器。 要求轮询状态寄存器直到它变为非零。 中殿和不正确的实施如下:
UINT1 * ptr = (UINT1 *) 0x1234; // Wait for register to become non-zero. while (*ptr == 0); // Do something else.
当你打开优化器时,这几乎肯定会失败,因为编译器会生成如下所示的汇编语言:
mov ptr, #0x1234 mov a, @ptr loop bz loop
const表示你的程序不会改变这个variables,但是正如文章中提到的那样,外部源可能和volatile会阻止它被优化掉。
* const volatile char * p =(const volatile char )0x30;
意思是: p的值仅由外部条件改变。
从概念上讲,你可以把这种types的variables看作逻辑查看器 。 在概念上类似于门上的窥视孔。 一个窥视孔允许你查看门的另一边是什么,但不允许你改变另一边( const )的内容。 然而,门外的条件可以根据自己的意愿改变(它们是不稳定的 )。 你可以看到发生了什么,但是你不能改变发生的事情。
例如,在embedded式系统中,有一些硬件寄存器用于提供有关外部事件发生的状态信息。 例如,用于检测RPM的光学编码器将在寄存器中设置一个值。 每旋转一次,它就会感应来自LED的光线,并在硬件寄存器中对其进行修改。 这是外部条件的意思。 在图片的另一面,也就是说在你的代码中(也许是一个PID控制循环),你可以阅读这些信息来提供对循环的调整,但是你不能改变这个值,你也不希望这样做。 ( const )
从你的软件的angular度来看,这说明了:
const
不会使variables不变。 它只是让编译器拒绝一些写入权限。 这仍然可以在variables中写入(例如,通过const-cast指针)。
你可以看到const
作为防止编码错误的保护。
这个声明确保你不会无意中写入p
,同时告诉编译器不要优化访问(caching,乱序执行(?),…),因为外部事件可能会写入p
。