请考虑以下事项:
int *volatile x;
(void)x;
GCC(从5.x到7.x)在启用-Wall
时抱怨它:
警告:' x'在此函数中使用未初始化的[-Wuninitialized]
铿锵声对此保持沉默。
出于某种原因,删除volatile
会消除警告。
标准是否表示甚至向volatile
投射void
指针未定义,而投射普通指针是正常的?或者这是一个GCC错误?
免责声明:此问题故意标记为C / C ++。海湾合作委员会对这两种语言都提出了同样的警告,我对这种情况感兴趣。
答案 0 :(得分:4)
像volatile
这样的普通旧数据类型的int *
行为之一是阻止编译器优化读取和写入变量。请注意int *
这里可能是float
或int
。
所以(void)x
意思是"读取x并且不对结果做任何事情"因为x
是volatile
。如果您阅读x
并且未将其固定到内存中的固定位置(编译器可能不知道,只有链接器可以知道),那么您实际上正在使用它未初始化< / em>的
如果它不是易失性的,虽然编译器可能仍然会读取x
,但它可能会避免/优化这个(因为它是无操作),并且警告静音。
clang在这里采取了安全的道路,并且由于链接器指令可以将变量x固定到某个位置(没有知道它的铿锵声),考虑到它没有值得在没有更多证据的情况下触发警告&#39;是一个问题。
答案 1 :(得分:2)
如果变量被声明为volatile
,那么就抛弃volatile
,就像从未声明的const变量中抛出const那样是未定义的行为。请参阅Annex J.2
的{{1}}:
在以下情况下,行为未定义:
- 尝试引用使用volatile限定类型定义的对象 使用具有非易失性限定类型的左值(6.7.3)。
在某处,我已阅读并注意到使用C Standard
的规则是:
对可能发生变化的变量使用volatile&#34;意外地&#34;。
在使用setjmp()的例程中对自动变量使用volatile。
要强制特定访问的易失性语义,请获取变量的地址并将其强制转换为(volatile WHATEVER *),取消引用强制转换表达式以获取值。
有时,volatile是解决某些领域中存在一致性问题的编译器中代码生成问题的合理方法,例如,x86上的gcc编译器具有赋值或转换为double的语义。不要随便这样做,因为如果不必要的代码质量很可能会下降。
除非你真的知道自己在做什么以及为什么要这样做,否则如果你使用不稳定的,你可能会做错事。尝试找到解决问题的另一种方法,如果仍然需要使用volatile代码 一个很好的小例子并发布到comp.lang.c并寻求有用的建议。
答案 2 :(得分:2)
对C和C ++中任何易失性对象的访问都是程序的可观察行为的一部分。 可观察行为是C和C ++中的一个重要概念。
您的代码正式读取了易失性指针x
。我猜想,当部分程序可观察行为涉及未初始化的值时,GCC认为这是一个相当严重的问题。
您删除volatile
的那一刻,x
的读取不再是可观察行为的一部分。因此警告也会消失。
答案 3 :(得分:1)
如果未对齐指针被取消引用,程序可能会异常终止。在某些体系结构中,如果所涉及的类型具有不同的对齐要求,则单独转换可能会导致信息丢失,即使该值未被解除引用。
C标准,6.3.2.3,第7段说:
指向对象或不完整类型的指针可以转换为 指向不同对象或不完整类型的指针。如果结果 指针未正确对齐引用的类型,行为 未定义。
答案 4 :(得分:1)
对于C ++,这由[expr]/12控制,它表示(void) x
将左值到右值的转换应用于x
(即,读取x
的值)仅当x
是volatile限定类型的glvalue时。因此,如果x
是volatile限定的,那么(void) x
将读取其值(这将产生不确定的值并触发未定义的行为)。如果x
不符合volatile标准,那么(void) x
不会应用左值到右值转换,并且行为定义明确。