指向堆栈变量的指针是否易失
Should a pointer to stack variable be volatile?
我知道我应该使用volatile
关键字告诉编译器不要优化对变量的内存读\写。我也知道,在大多数情况下,它应该只用于与非C++记忆通信。
但是,我想知道在持有指向某个局部(堆栈)变量的指针时是否必须使用volatile
。
例如:
//global or member variable
/* volatile? */bool* p_stop;
void worker()
{
/* volatile? */ bool stop = false;
p_stop = &stop;
while(!stop)
{
//Do some work
//No usage of "stop" or p_stop" here
}
}
void stop_worker()
{
*p_stop = true;
}
在我看来,具有一定优化级别的编译器可能会看到stop
是一个局部变量,永远不会更改,并且可以用while(true)
替换while(!stop)
,从而在什么都不做的情况下更改*p_stop
。
那么,在这种情况下是否需要将指针标记为易失性?
PS:请不要教我为什么不使用它,使用这个黑客的真正代码这样做是出于(复杂解释)的原因。
编辑:
我没有提到这两个函数在不同的线程上运行。
worker()
是第一个线程的函数,应使用p_stop
指针从另一个线程停止它。我不想知道有什么更好的方法来解决这种黑客背后的真正原因。我只是想知道这是否是C++中定义\未定义的行为(就此而言为 11),以及这是否依赖于编译器\平台\等。到目前为止,我看到@Puppy说每个人都错了,这是错误的,但没有参考表示这一点的具体标准。
我知道你们中的一些人被"不要教训我"部分冒犯了,但请坚持真正的问题 - 我应该使用volatile
还是UB? 如果你能通过提供完整的答案来帮助我(和其他人)学习新的东西。
我只是想知道这是否是C++中定义\未定义的行为(就此而言为 11)
Ta-da(来自N3337,"准C++11")
如果两个表达式计算修改内存位置[..],而另一个表达式访问或修改相同的内存位置,则两个表达式计算将发生冲突。
§1.10/4
和:
如果程序在不同线程中包含两个冲突的操作,则程序的执行包含数据争用,其中至少一个不是原子的,并且两者都不会先于另一个发生。任何此类数据争用都会导致未定义的行为。[..]
§1.10/21
您正在从不同的线程访问对象stop
(内存位置),两次访问都不是原子的,因此也没有"发生之前"的关系。简而言之,您有数据竞争,因此存在未定义的行为。
我不想知道有什么更好的方法来解决这种黑客背后的真正原因。
原子操作(由C++标准定义)是(可靠地)解决此问题的唯一方法。
那么,在这种情况下是否需要将指针标记为易失性?
不。这不是必需的,主要是因为易失性甚至不能远程覆盖在这种情况下需要它做什么。必须使用实际的同步基元,如原子操作或互斥锁。在这里使用volatile
是未定义的行为,您的程序将爆炸。
volatile
对并发没有用。它可能对实现并发原语很有用,但远远不够。
坦率地说,是否要使用实际的同步原语无关紧要。如果你想写正确的代码,你别无选择。
PS:请不要教我为什么不使用这个,
我不确定我们应该说什么。编译器管理堆栈,因此您对堆栈执行的任何操作在技术上都是未定义的行为,并且在升级到下一版本的编译器时可能无法正常工作。
您还正在做出的假设可能与编译器优化时的假设不同。这是使用(或不使用)volatile
的真正原因;您可以向编译器提供指导,帮助它确定优化是否安全。使用volatile
告诉编译器,它应该假定这些变量可能会由于外部影响(其他线程或特殊硬件行为)而更改。
所以是的,在这种情况下,看起来您需要用volatile
限定符标记p_stop
和stop
。
(注意:这是必要的,但还不够,因为它不会导致在具有宽松内存模型的语言实现中发生适当的行为,该模型需要屏障来确保正确性。见 https://en.wikipedia.org/wiki/Memory_ordering#Runtime_memory_ordering)
这个问题根本无法从提供的细节中得到回答。
如问题中所述,这是一种完全不支持的线程之间通信方式。
所以唯一的答案是:
指定您正在使用的编译器版本,并希望有人知道其最黑暗的秘密或参考您的文档。所有C++标准都会告诉你这是行不通的,任何人都可以告诉你的是"可能有效但不起作用"。
没有"哦,来吧,伙计们,每个人都知道它几乎有效,我该怎么做作为解决方法?眨眨眼"的回答。
除非编译器不支持原子或适当的并发机制,否则没有合理的理由这样做。 "它不受支持"不是"解释复杂",所以我会根据该代码片段着迷于了解不正确执行此操作的可能原因(古代编译器除外)。
- 新分配指向函数的指针是否合法?
- 在函数结束后使用指向变量的指针是否安全?
- C++ 返回指向函数内定义的静态数组的指针是否有效?
- 经过最后一个数组元素末尾的指针是否应该等于超过整个数组末尾的指针?
- 传递给放置 new 的指针是否是指向其对象表示形式的非 UB 指针?
- 'this'指针是否可以在 c++ 标头声明中使用?
- C++ 在向量中使用指针是否可能导致指针悬空?
- 唯一指针是否在堆或堆栈上分配内存?
- 将基本实例指针强制转换为派生实例指针是否合法?(实例不是派生实例)
- 当向量增长时,指向向量元素的C++指针是否无效
- 如何判断指针是否是指向数组的指针
- 在类方法中使用 "this" 指针是否是一种好的做法?
- 如何知道指针是否已在其他地方释放
- 结构成员指针是否自动初始化为零?
- 为静态类创建指向对象的指针是否合法?
- 新的自动关键字指针是否会自行删除?
- 创建指针是否超过非数组指针的末尾,而不是从 C++17 中的一元运算符和未定义的行为派生?
- 检查指针是否指向矢量元素
- 如何创建一个带锁的指针数组并检查指针是否空闲?
- 在映射中插入更多元素后,指向 QMap 中元素的指针是否仍然有效?