指针声明为常量和易失性

ame*_*yCU 63 c const volatile

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

const volatile char *p=(const volatile char *) 0x30;
Run Code Online (Sandbox Code Playgroud)

p的值仅由外部条件改变

我没有得到什么是外部条件.还有这种宣言的实际用途是什么?

Bla*_*iev 59

const说,你的程序的流程是不是要修改什么是指向p.在取消引用指针后修改该值的任何尝试都将导致编译时错误:

*p = 'A'; // will not compile
Run Code Online (Sandbox Code Playgroud)

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

volatile char *q = 0x30;
*q = 'A'; // will compile
Run Code Online (Sandbox Code Playgroud)

要打破这个合同的另一种方式是虚掷了const来自p:

*(volatile char *) p = 'A'; // will compile
Run Code Online (Sandbox Code Playgroud)

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) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

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

  • 是的,如果编译器确定它将值保留在寄存器中并且外部设备修改指定位置的内存,则更改将不会反映在寄存器中,因为它是一个单独的存储位置.`volatile`意在防止这种情况. (8认同)

Dev*_*lar 23

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

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

它是只读的,所以你不应该写它.因此,const.


Sou*_*osh 22

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

声明的对象

extern const volatile int real_time_clock;

可以由硬件修改,但不能分配,递增或递减.

另外,相关脚注(134),

易失性声明可用于描述与存储器映射的输入/输出端口或由异步中断功能访问的对象相对应的对象.对如此声明的对象的操作不应由实现"优化"或重新排序,除非评估表达式的规则允许.

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

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

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

  • @ameyCU:如果变量不是`volatile`,编译器可以假设`msg`的内容在编译代码和代码检查这些内容之间不能改变; 在一些系统上,硬件可以保证`msg`在代码开始执行和代码检查之间不能改变.另一方面,让部分构建系统在二进制文件中搜索诸如"supercalifraglisticexpialodicious"[或者可能是GUID]之类的密钥并替换其他内容有时可能是有用的.问题是,海湾合作委员会的代码是否需要"看到"这种变化. (2认同)

Sha*_*our 11

我们可以使用volatile关键字的文章简介:

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

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

和:

嵌入式系统包含真实硬件,通常具有复杂的外围设备.这些外设包含寄存器,其值可能与程序流异步变化.举一个非常简单的例子,考虑地址为0x1234的8位状态寄存器.您需要轮询状态寄存器,直到它变为非零.中殿和不正确的实现如下:

UINT1 * ptr = (UINT1 *) 0x1234;

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

一旦打开优化器,这几乎肯定会失败,因为编译器将生成如下所示的汇编语言:

mov    ptr, #0x1234     mov    a, @ptr loop     bz    loop
Run Code Online (Sandbox Code Playgroud)

const表示你的程序不会改变变量,但正如文章中提到的那样,外部资源可以和volatile一起防止它被优化掉.


ryy*_*ker 7

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

从概念上讲,您可以将此类变量视为逻辑查看器.概念与门中的窥视孔相似.窥视孔允许您查看门另一侧的内容,但不允许您更改另一侧的内容(常量).然而,门外的条件可以根据自己的意愿改变(它们是不稳定的).你可以看到会发生什么,但你不能改变发生的事情.

例如,在嵌入式系统中,存在硬件寄存器,其被设计为提供关于外部世界中发生的事件的状态信息.例如,用于感测RPM的光学编码器将在寄存器中设置值.每次旋转,它都会感应来自LED的光并修改硬件寄存器中的值.这就是外部条件的含义.在图片的另一侧,即在您的代码中(可能是PID控制循环),您可以读取此信息以用于提供对循环的调整,但您不能更改此值,也不想.(const)

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

在此输入图像描述

  • 我真的想象着窥视孔的东西.:) (2认同)

joh*_*n d 6

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

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

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