指向堆栈变量的指针应该是易失性的吗?
指向堆栈变量的指针应该是易失性的吗?
我知道我应该使用volatile
关键字告诉编译器不要对变量的内存读写进行优化。我也知道在大多数情况下,它只应该用于与非C++内存交互。\n然而,我想知道当持有指向某些局部(栈)变量的指针时,是否必须使用volatile
。\n例如:\n
//全局或成员变量 /* volatile? */bool* p_stop; void worker() { /* volatile? */ bool stop = false; p_stop = &stop; while(!stop) { //做一些工作 //这里不使用"stop"或"p_stop" } } void stop_worker() { *p_stop = true; }
\n对于编译器来说,stop
是一个从不改变的局部变量,它可能会将while(!stop)
替换为while(true)
,从而导致*p_stop
无效。\n因此,在这种情况下,是否需要将指针标记为volatile?\nP.S:请不要对我为什么不使用此方法进行讲解,使用此技巧的真实代码有一些(难以解释的)原因。\n编辑:\n
- \n
- 我忘记提到这两个函数在不同的线程上运行。\n
worker()
是第一个线程的函数,应该通过p_stop
指针从另一个线程停止它。\n - 我对于解决这种技巧背后真正原因的更好方法不感兴趣。我只想知道在C++(具体来说是C++11)中,这是定义还是未定义行为,以及是否取决于编译器、平台等。到目前为止,我看到@Puppy说大家都错了,这是错误的,但没有引用任何具体标准来证明这一点。\n
\n
\n
\n我理解有些人对“不要对我进行讲解”部分感到冒犯,但请专注于真正的问题 - 我应该使用volatile
还是不用?这是否是未定义行为?如果可能,请帮助我(和其他人)通过提供一个完整的答案来学到新东西。
指向堆栈变量的指针是否应该是volatile的问题出现的原因是,使用指向堆栈变量的指针可能导致不确定行为,并且在升级到编译器的下一个版本时可能无法正常工作。此外,使用指向堆栈变量的指针时,您可能会做出与编译器优化时假设不同的假设。这就是使用(或不使用)volatile的真正原因;您向编译器提供指导,帮助其确定优化是否安全。使用volatile告诉编译器,它应该假设这些变量可能由于外部影响(其他线程或特殊硬件行为)而发生变化。
因此,在这种情况下,您需要使用volatile限定符来标记p_stop和stop这两个变量。
(注意:这是必要的,但不足以在具有松散内存模型并要求使用屏障以确保正确性的语言实现中引发适当的行为。请参见https://en.wikipedia.org/wiki/Memory_ordering#Runtime_memory_ordering)
这个问题的解决方法是使用volatile限定符来标记指向堆栈变量的指针。这样做可以告诉编译器,它应该假设这些变量可能由于外部影响而发生变化,从而避免优化问题。
值得注意的是,volatile语义仅影响编译器的优化,它们不强制执行任何与底层机器内存模型(屏障等)相关的行为。因此,volatile不能解决所有问题,特别是在涉及非原子操作时会出现问题。在这种情况下,可以考虑使用std::atomic来确保正确性。
总之,指向堆栈变量的指针是否应该是volatile的问题的出现是因为使用指向堆栈变量的指针可能导致不确定行为,并且在编译器优化时可能与编译器的假设不同。解决方法是使用volatile限定符来标记指针,并考虑使用std::atomic来确保正确性。
指针指向堆栈变量是否需要标记为volatile?这个问题的出现是因为volatile在这种情况下根本无法满足你的需求。你必须使用实际的同步原语,比如原子操作或互斥锁。在这里使用volatile是未定义行为,你的程序将会崩溃。
volatile对于并发是无用的。它可能对实现并发基元有用,但远远不够。无论你是否想使用实际的同步原语都是无关紧要的。如果你想编写正确的代码,你别无选择。
取决于底层架构...具有强内存模型的单核微控制器应该能够使用volatile正常工作,而不需要使用重量级的同步原语。
根本不是。C++标准定义了C++语言中可接受的内容。你的实现所使用的硬件是否提供了更强的保证是无关紧要的。
有趣...我认为你在理论上是对的,尽管在实践中某些C/C++编译器可能提供与单核处理器的顺序内存访问一致的内存语义。你对这个问题有什么了解吗?我找到了这篇文章:preshing.com/20120930/weak-vs-strong-memory-models "正如我之前展示的,你仍然必须指定正确的内存顺序约束,即使只是为了防止编译器重新排序。"但我找不到他在哪里给出了编译器所需的内存顺序约束的示例,即使在强硬件上也是如此。
创建具体示例很棘手,因为编译器只是生成大多数时间按照你的期望工作但在细微和非确定性方式下出错的机器代码。看一下aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf。虽然现在略微过时,但在这种情况下,情况并没有真正改变-C++11主要使得使用原子操作正确成为可能,而不是改变volatile完全无法满足这种用法的方式。