C 指针声明为常量和易失性

C 指针声明为常量和易失性,c,constants,volatile,C,Constants,Volatile,在阅读时,我遇到了这种类型的声明和下面的一行- const volatile char *p=(const volatile char *) 0x30; p值仅由外部条件改变 我不明白什么是外部条件。这类声明的实际用途是什么?const表示程序流不会修改p所指的内容。在取消引用指针后修改值的任何尝试都将导致编译时错误: *p = 'A'; // will not compile 请注意,这不是一个特别强大的合同;位置0x30处的值仍然可以通过别名非常量指针更改,而不是p: volatile

在阅读时,我遇到了这种类型的声明和下面的一行-

const volatile char *p=(const volatile char *) 0x30;
p值仅由外部条件改变


我不明白什么是外部条件。这类声明的实际用途是什么?

const表示程序流不会修改
p
所指的内容。在取消引用指针后修改值的任何尝试都将导致编译时错误:

*p = 'A'; // will not compile
请注意,这不是一个特别强大的合同;位置
0x30
处的值仍然可以通过别名非常量指针更改,而不是
p

volatile char *q = 0x30;
*q = 'A'; // will compile
*(volatile char *) p = 'A'; // will compile
另一种打破合同的方法是从
p
中去掉
const

volatile char *q = 0x30;
*q = 'A'; // will compile
*(volatile char *) p = 'A'; // will compile
但是,
volatile
并不排除由另一个线程、内核、异步信号处理程序或访问相同内存空间的外部设备引起的任何修改。这样,编译器就不会错误地假设
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) {
    ...
}

如果编译器错误地优化了这样一个结构,它可能会发出指令,从内存中只加载一次值,然后将其保存在寄存器中。寄存器本质上是一个独立的副本,对原始位置的任何更改都不会反映在那里,而且,不用说,这可能会导致一些非常严重的错误。

例如,考虑网卡的只读硬件寄存器

它可能会在程序的控制之外更改,因此不允许编译器将其值缓存在寄存器中或对其进行优化。因此,
volatile

而且它是只读的,所以你不应该给它写信。因此,
const

我们可以使用这样一篇文章:

只要变量的值可能发生变化,就应该将其声明为volatile 意外地实际上,只有三种类型的变量可以改变:

  • 内存映射外围寄存器
  • 由中断服务例程修改的全局变量
  • 多线程应用程序中的全局变量
以及:

嵌入式系统包含真正的硬件,通常具有复杂的 外围设备。这些外围设备包含寄存器,其值可能 异步更改程序流。作为一个非常简单的例子, 考虑地址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表示您的程序不会更改变量,但正如文章中所述,外部源代码可能会更改变量,而volatile会阻止对其进行优化。

const
不会使变量保持常量。这只会使编译器拒绝一些写访问。这仍然可以写入变量(例如通过const casted指针)

您可以将
const
视为防止编码错误的保护措施


此声明确保您不会无意中写入
p
,同时告诉编译器不要优化访问(缓存、无序执行(?)…),因为外部事件可能会写入
p

。首先,让我引用
C11
标准第§6.7.3章类型限定符中的示例

声明的对象

extern const volatile int实时时钟

可以由硬件修改,但不能指定、递增或递减

此外,相关脚注(134)

volatile声明可用于描述与内存映射输入/输出端口相对应的对象或异步中断函数访问的对象。除非计算表达式的规则允许,否则对如此声明的对象的操作不得通过实现“优化”或重新排序

这意味着,硬件可以修改变量的值(通过内存映射),但不能“以编程方式”修改

因此,这里的优势是双重的

  • 无论何时使用该值,都将从内存中读取(不允许缓存),从而为您提供最新更新的值(如果已更新)
  • 程序不能有意或无意地更改(写入)该值

*const volatile char*p=(const volatile char)0x30
其含义是:p的值仅因外部条件而改变

从概念上讲,您可以将这种类型的变量视为逻辑查看器。在概念上类似于门上的窥视孔。窥视孔允许您查看门另一侧的内容,但不允许您更改另一侧的内容(const)。但是,门外侧的条件可以自行改变(它们是不稳定的)。你可以看到发生了什么,但你不能改变发生了什么

例如,在嵌入式系统中,有一些硬件寄存器设计用于提供有关外部世界发生的事件的状态信息。例如,用于检测RPM的光学编码器将在寄存器中设置一个值。每次旋转,它都会感应来自led的光线,并修改硬件寄存器中的值。这就是所谓的外部条件。在图片的另一侧,即在代码(可能是PID控制循环)中,您可以读取此信息以用于提供循环调整,但您不能更改此值,也不希望更改此值。(常数)

从软件的角度来看,这说明:

这“…因为外部进程可能写入
p