考虑这两个定义:
volatile int a[10];
volatile int *p = a;
编辑:澄清问题。
如果我们引用
a[3]
,内存位置将被视为volatile
存储,也就是说,编译器不会假设它保持不变。
对于数组,很明显其意图是将数组的整个内容视为
volatile
。
在 C 中,
p[3]
也指代 volatile
存储吗,尽管 p
被声明为指针,而不是数组?换句话说,在使用“指向 volatile
的指针”的表达式中,例如 p[3]
或 *(p + 3)
是 volatile
“传染性”,并且它是否适用于除 所指向的确切位置之外的内存位置指向 volatile
? 的指针
我确实知道编译器将内存视为易失性意味着什么。
在C中是不是p[3]也指易失性存储...
是的,
volatile int *p
使p
成为指向 volatile int 的指针。因此,使用 p
(又名 p
的取消引用)进行的任何访问都将被视为对易失性内存的访问。
请注意,
p
本身不是易失性的。只有 p
所指向的才是不稳定的。
这并不是一个证明,但为了好玩,您可以尝试从
p
中删除“易失性”,例如:
volatile int a[10];
int *p = a;
用例如编译这个
gcc
会向您发出如下警告:
warning: initialization discards 'volatile' qualifier from pointer target type [-Wdiscarded-qualifiers]
这很清楚地表明出了问题,因为
p
不再指向 volatile int。
顺便说一句: 常量指针与指向常量的指针可能会是一本有趣的读物。是关于
const
而不是 volatile
但原理是一样的。
我不确定为什么每个人都在谈论“易失性存储”,好像它意味着一些特殊的东西。
volatile
只是 C 语言中的一个关键字,告诉编译器不要对变量做出任何假设,永远不要优化它,并且总是在使用它之前从内存中重新加载它。就是这样。最常见的用法是与硬件相关的编程:内存映射硬件寄存器、DMA 缓冲区等。
要指向声明为
volatile
的变量,指针还必须具有代码中的 volatile
限定符。当限定符(volatile
或const
等)放置在指针声明中*
的左侧时,它指的是指向的数据。因此:
volatile* int x; // pointer to volatile data
int* volatile y; // volatile pointer to non-volatile data
“抛弃”限定符通常是未定义的行为。另外,赋值规则规定,对于
x = y
,x
必须至少具有 y
的所有限定符,但不一定是相反的方式。
对于数组,在大多数表达式中使用时,它们会“衰减”为指向第一个元素的指针。而“decay”得到的指针带有数组项的所有限定符。因此,在您的情况下,当在大多数表达式中使用时,
a
将变成volatile int*
。
每当通过
volatile
限定指针访问对象时,都适用与直接访问对象时相同的规则。
(到目前为止,C 标准中存在一个小问题,最终将在即将推出的 C23 中得到修复。到目前为止,C 标准已经谈到对声明的易失性对象的访问算作副作用。在 C23 中,此问题已修复为包括所有易失性限定类型的左值访问。)
与流行的看法相反,声明
volatile int
不会产生任何易失性存储,它只是告诉编译器将其视为如此。
volatile
不适用于 x86 以外的多线程问题,因为其他处理器的内存模型较弱,并且编译器不会发出必要的指令。 volatile
只告诉编译器不要优化变量访问,不要发出内存屏障。
将会发生的情况是,对
a
的任何访问或通过 p
无论其指向何处的任何访问都不会被优化掉。就是这样,不多也不少。
实际的易失性存储器是这样声明的:
extern volatile int trisa;
并且定义是由无法用标准c编写的目标文件提供的。 volatile
是 for 内存映射 IO。它还有其他目的,但这就是它被创建的原因。当 setjmp()
中的规范引用 volatile
时,它知道编译器不知道的东西; setjmp()
会破坏调用保留的寄存器,因此将本地变量视为易失性内存可以正常工作。