请考虑以下代码:
int square(volatile int *p)
{
return *p * *p;
}
现在,volatile
关键字表示a中的值
内存位置可以以编译器未知的方式更改或具有
其他未知的副作用(例如通过信号中断修改,
硬件寄存器,或内存映射I / O)即使没有
程序代码修改内容。
那么当我们将指针声明为volatile时会发生什么?
上述代码是否始终有效,或者与此有何不同:
int square(volatile int *p)
{
int a = *p;
int b = *p
return a*b;
}
我们可以最终乘以不同的数字,因为指针是易变的吗?
或者有更好的方法吗?
答案 0 :(得分:11)
是的,您当然可以使用易失性指针。
易失性意味着不多于且不少于对易失性对象(无论何种类型)的每次访问都被视为可见的副作用,因此免于优化(特别是,这意味着访问可能不会被重新排序)或完全折叠或优化)。这对于读取或写入值,调用成员函数以及当然也用于解除引用也是如此。
请注意,当前一段说明"重新排序"时,假定单个执行线程。易失性不能替代原子操作或互斥锁/锁。
更简单的说法是,volatile
通常会转换为“不要优化”,就像我说的那样#34;。
在指针的上下文中,请参阅Chris Lattner所熟知的示例性使用模式"每个程序员需要了解的有关未定义行为的信息" article(是的,那篇文章是关于C,而不是C ++,但同样适用):
如果您正在使用基于LLVM的编译器,则可以取消引用" volatile" null指针,如果你正在寻找的话,会发生崩溃,因为优化器通常不会触及易失性加载和存储。
答案 1 :(得分:11)
指针可以是
volatile
吗?
绝对;任何类型,不包括函数和引用,可以volatile
- 限定。
请注意,易失性指针声明为 T *volatile
,而不是volatile T*
,而是声明指针 -volatile < /强>
易失性指针意味着指针值(即其地址而不是指向的值)可能具有编译器在访问时不可见的副作用;因此,对于那些访问,可能不会考虑源自“as-if规则”的优化。
int square(volatile int *p) { return *p * *p; }
编译器不能假设读取*p
获取相同的值,因此不允许在变量中缓存其值。如你所说,结果可能会有所不同,而不是*p
的平方。
具体示例:假设您有两个int
s
int a1 [] = { 1, 2, 3, 4, 5 };
int a2 [] = { 5453, -231, -454123, 7565, -11111 };
和指向其中一个的指针
int * /*volatile*/ p = a1;
对尖头元素进行一些操作
for (int i = 0; i < sizeof(a1)/sizeof(a1[0]); ++i)
*(p + i) *= 2;
这里p
必须在每次迭代时读取volatile
,因为它可能实际上由于外部事件而指向a2
。
答案 2 :(得分:2)
是。 int * volatile
。
在C ++中,根据类型/指针/引用的关键字在令牌之后,如int * const
是指向整数的常量指针,int const *
是指向常量整数的指针,int const * const
是常量指针到常数整数等只有当第一个标记符号为const int x
时,您才可以在类型之前编写关键字。
答案 3 :(得分:2)
volatile
关键字是编译器(7.1.6.1/7)的提示:
注意: 挥发物 是对实现的暗示,以避免涉及对象的激进优化 因为对象的值可能会被实现无法检测的方式更改。此外, 对于某些实现, 挥发物 可能表示需要访问特殊硬件指令 物体。看到 1.9 详细的语义。一般来说,语义 挥发物 本打算是 同样在C中 ++ 因为他们在C. - 结束说明 ]
这是什么意思?好吧,看看这段代码:
bool condition = false;
while(!condition)
{
...
}
默认情况下,编译器将轻松优化条件(它不会更改,因此无需在每次迭代时检查它)。但是,如果您将条件声明为volatile
,则不会进行优化。
所以当然你可以有一个易失性指针,并且可以编写因此而崩溃的代码,但变量是volative
的事实并不意味着它必然会是因外部干扰而改变。
答案 4 :(得分:1)
您可能最终会将不同的数字相乘,因为它不稳定且可能会意外更改。所以,你可以尝试这样的事情:
int square(volatile int *p)
{
int a = *p;
return a*a;
}
答案 5 :(得分:1)
是的,如果指针变量指向的变量可能会意外地发生变化,即使这可能发生的变化在代码中也不明显,指针也可能是易失性的。
一个示例是一个对象,可以通过控制线程外部的东西进行修改,并且编译器不应该进行优化。
最有可能使用volatile说明符的地方是低级代码,它直接处理硬件以及可能发生意外更改的位置。