(与这个问题有关,抛弃挥发物是否安全?,但不完全相同,因为该问题与特定实例有关)
是否有过在那里虚掷的情况下volatile是不被认为是危险的做法?
(一个特定的例子:如果声明了一个函数
void foo(long *pl);
Run Code Online (Sandbox Code Playgroud)
我必须实施
void bar(volatile long *pl);
Run Code Online (Sandbox Code Playgroud)
有()我需要执行栏的一部分来调用foo(PL),然后好像我不能得到这个作为是工作,因为假设由foo的编制()和bar的调用者的编辑制作()不兼容.)
作为推论,如果我有一个volatile变量v,我想打电话给foo(&v)别人的功能void foo(long *pl),那人告诉我这是安全的,我可以调用之前投的指针,我的直觉是要告诉他们,他们错了,因为没有办法保证,void foo(volatile long *pl)如果他们想支持使用volatile变量,他们应该更改声明.我们哪一个是正确的?
Ant*_*ams 17
如果声明 了变量volatile那么它就是未定义的行为来抛弃volatile它,就像它是未定义的行为来抛弃const声明的变量一样const.见C标准的附录J.2:
在以下情况下,行为未定义:
...
- 尝试通过使用具有非const限定类型的左值来修改使用const限定类型定义的对象(6.7.3).
- 尝试通过使用具有非易失性限定类型的左值来引用使用volatile限定类型定义的对象(6.7.3).
但是,如果您只有volatile指针volatile或非volatile变量的引用,那么您可以自由地抛弃volatile.
volatile int i=0;
int j=0;
volatile int* pvi=&i; // ok
volatile int* pvj=&j; // ok can take a volatile pointer to a non-volatile object
int* pi=const_cast<int*>(pvi); // Danger Will Robinson! casting away true volatile
int* pj=const_cast<volatile int*>(pvj); // OK
*pi=3; // undefined behaviour, non-volatile access to volatile variable
*pj=3; // OK, j is not volatile
Run Code Online (Sandbox Code Playgroud)
seh*_*ehe 10
一旦该值实际上不再是不稳定的,抛弃volatile将是可以的.在SMP /多线程情况下,在获取锁定(并传递内存屏障,获取锁定时最常隐含)之后,这可能会成为现实.
所以这是一个典型的模式
volatile long *pl = /*...*/;
//
{
Lock scope(m_BigLock); /// acquire lock
long *p1nv = const_cast<long *>(p1);
// do work
} // release lock and forget about p1nv!
Run Code Online (Sandbox Code Playgroud)
但我可以提出一些其他情况,其中价值观不再波动.我不会在这里建议他们,因为我相信如果你知道你在做什么,你可以自己拿出来.否则,锁定方案似乎足够坚固以提供示例
通过签名foo(long *pl),程序员声明他们不期望指向的long值在执行期间从外部改变foo.传递一个指向long该值是在整个调用被同时修改甚至可能会导致错误的行为,如果编译器发出指针多次提领由于缺乏寄存器,并通过它选择不存储在第一非关联化值码堆栈.例如,在:
void foo(long *pl) {
char *buf = (char *) malloc((size_t) *pl);
// ... busy work ...
// Now zero out buf:
long l;
for (l = 0; l < *pl; ++l) {
buf[l] = 0;
}
free(buf);
}
Run Code Online (Sandbox Code Playgroud)
foo如果在long执行繁忙工作时增加值,则可以在"零输出buf"步骤中超出缓冲区.
如果该foo()函数应该以原子方式递增long指向的值pl,则该函数将不正确,long *pl而不是volatile long *pl因为该函数明确要求对long值的访问是序列点.如果foo()仅以原子方式递增,则该函数可能有效,但它不正确.
评论中已经提出了两个解决这个问题的方法:
裹foo走long *在过载服用volatile long *:
inline void foo(volatile long *pvl) {
long l = *pvl;
foo(&l);
*pvl = l;
}
Run Code Online (Sandbox Code Playgroud)将声明更改foo为void foo(volatile long *pvl).