在阅读过程中,我遇到了这种声明类型和以下的一行代码 -
const volatile char *p=(const volatile char *) 0x30;
p的值仅受外部条件影响
我不明白所谓的“外部条件”是什么。这种声明有什么实际用途?
const
表示程序的流程不会修改p
所指向的内容。在解引用指针后尝试修改该值的任何操作都将导致编译时错误:
*p = 'A'; // will not compile
p
以外,通过别名非 const 指针仍然可以更改位于 0x30
处的值:volatile char *q = 0x30;
*q = 'A'; // will compile
另一种打破这个合同的方法是从 p
中弃掉 const
:
*(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
的作用就是避免这种情况发生。 - Blagovest Buyuklievvolatile
——在这里,寄存器由编译器与内存同步。 - DevSolar考虑一个只读的硬件寄存器,例如您的网络卡。
它可能会在程序控制之外发生改变,因此编译器不允许将其值缓存在寄存器中或优化掉。因此使用 volatile
。
而且它是只读的,所以你不应该对它进行写操作。因此使用 const
。
首先,让我引用来自标准、第6.7.3章节,类型限定符的例子
声明了一个对象
extern const volatile int real_time_clock;
该对象可能由硬件修改,但不能被赋值、增加或减少。
另外,相关的脚注(134),
volatile声明可以用于描述与内存映射输入/输出端口相对应的对象或由异步中断函数访问的对象。对这样声明的对象的操作不得被实现优化掉或重新排序,除非允许按照计算表达式的规则进行。
也就是说,变量的值可以通过硬件(通过内存映射)进行修改,但不能通过编程方式进行修改。
因此,在这里有两个优点:
volatile const char msg[] = "supercalifragilisticexpealidocious"; char const *const msg2 = "us";
,并且使用实用程序在生成的二进制文件中找到了msg
的地址并进行了修补,那么编译器是否需要在运行时读取该内存的内容(而不是假设它包含指定的字符串),并且避免将其存储用于其他任何事情[例如由msg2指向的“us”文字]? - supercatmsg
和msg2
不能共享,因为您明确指定msg
可能会受到外部影响而发生更改--如果它们被共享,那么非易失性的msg2
也会随之更改。 - DevSolar我们可以使用文章 关于volatile关键字的介绍,该文章指出:
当一个变量的值可能会意外改变时,应将其声明为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 char *p=(const volatile char ) 0x30;
什么是“p的值仅由外部条件改变”的意思。
概念上,您可以将这种类型的变量视为逻辑查看器。类似于门上的窥视孔的概念。 一个窥视孔允许您查看门的另一侧发生了什么,但不允许您更改另一侧发生的内容(const)。但是,门外的条件可能会自行更改(它们是volatile)。您可以看到发生了什么事情,但不能更改发生的事情。
例如,在嵌入式系统中,有硬件寄存器专门用于提供与外部世界中发生事件的状态信息。例如,用于感知RPM的光学编码器会在寄存器中设置一个值。每次旋转时,它会感知来自LED的光并修改硬件寄存器中的值。这就是所谓的外部条件。在另一方面,在您的代码中(例如PID控制循环),您可以读取此信息以用于提供对该循环的调整,但您不能更改此值,也不希望进行更改(const)。
从您的软件角度来看,这说明了:
volatile
之间制造的虚假联系 :D - Lightness Races in Orbitconst
并不会使变量本身成为常量,它只是告诉编译器拒绝一些写入操作。这意味着,仍然可以通过使用const
强制类型转换指针来写入变量。
你可以把const
看作是对编码错误的一种保护措施。
这个声明确保你不会意外地向p
写入数据,同时告诉编译器不要优化访问(如缓存、乱序执行等),因为外部事件可能会对p
进行写入操作。
p
。*" 存在歧义,因为它可能不是指任何正在运行的程序中的"进程"。请参考Sourav Ghosh的答案(https://dev59.com/J1wZ5IYBdhLWcg3wbv_r#31456549) 了解原因。 - alk
const
只是意味着你不能改变它。考虑int a=5;
和int& x=a;
,你仍然可以执行a=6;
(这会影响到x
!) - Alec Teala
没有被声明为const
。你是不是想说const int a=5;
? - ApproachingDarknessFishconst int& x
- 你不能通过x
改变a
,但是a
仍然可以改变! - Alec Teal