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

Pointer declared as constant as well as volatile

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

const volatile char *p=(const volatile char *) 0x30;

The value of p is changed by external conditions only

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

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

*p = 'A'; // will not compile

请注意,这不是一个特别强大的合同;位置 0x30 的值仍然可以通过别名非常量指针更改,而不是 p:

volatile char *q = 0x30;
*q = 'A'; // will compile

打破这个契约的另一种方法是从 p 中丢弃 const:

*(volatile char *) p = 'A'; // will compile

然而,volatile 不排除任何可能由另一个线程、内核、异步信号处理程序或可以访问同一内存的外部设备引起的修改 space.这样编译器就不会错误地假设 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.

我们可以使用文章 Introduction to the volatile keyword 说:

A variable should be declared volatile whenever its value could change unexpectedly. In practice, only three types of variables could change:

  • Memory-mapped peripheral registers
  • Global variables modified by an interrupt service routine
  • Global variables within a multi-threaded application

和:

Embedded systems contain real hardware, usually with sophisticated peripherals. These peripherals contain registers whose values may change asynchronously to the program flow. As a very simple example, consider an 8-bit status register at address 0x1234. It is required that you poll the status register until it becomes non-zero. The nave and incorrect implementation is as follows:

UINT1 * ptr = (UINT1 *) 0x1234;

// Wait for register to become non-zero.
while (*ptr == 0);
// Do something else.

This will almost certainly fail as soon as you turn the optimizer on, since the compiler will generate assembly language that looks something like this:

mov    ptr, #0x1234     mov    a, @ptr loop     bz    loop

const 表示您的程序不会更改该变量,但正如文章中所述,外部来源可以,而 volatile 会阻止它被优化掉。

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

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

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

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

An object declared

extern const volatile int real_time_clock;

may be modifiable by hardware, but cannot be assigned to, incremented, or decremented.

此外,相关脚注 (134),

A volatile declaration may be used to describe an object corresponding to a memory-mapped input/output port or an object accessed by an asynchronously interrupting function. Actions on objects so declared shall not be "optimized out" by an implementation or reordered except as permitted by the rules for evaluating expressions.

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

所以,这里的优势是双重的,

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

*const volatile char *p=(const volatile char ) 0x30;
什么意思:p 的值仅由外部条件改变。

从概念上讲,您可以将此类变量视为逻辑查看器。在概念上类似于门上的窥视孔。窥视孔允许您查看门另一侧的内容,但不允许您更改另一侧的内容 (const)。然而,门外的条件可以自行改变(它们是 volatile)。你可以看到会发生什么,但你无法改变正在发生的事情。

例如,在嵌入式系统中,有硬件寄存器旨在提供有关外部世界发生的事件的状态信息。例如,用于感测 RPM 的光学编码器将在寄存器中设置一个值。每次旋转,它都会感应来自 LED 的光并修改硬件寄存器中的值。这就是外在条件的意思。在图片的另一边,即在您的代码中(可能是 PID 控制回路),您可以读取此信息以用于对回路进行调整,但您无法更改此值,您也不想更改。 (常量)

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