为什么在函数中丢弃volatile限定符会调用警告?

时间:2013-04-15 01:01:47

标签: c warnings volatile

(在我开始之前:我知道这个主题有existing questions,但我没有找到回答为什么这是一个问题。我做的它经常,并想知道我是否正在创造潜在的问题。)

我很好奇为什么在函数调用中丢弃volatile限定符会引发编译器警告。

情况如下:

volatile uint8_t thingy;
void awesome_function(uint8_t *arg);

awesome_function(&thingy); << warning

现在,我的理解是volatile限定符将变量标记为可能在编译器控件之外的方式发生变化的变量。因此,某些优化(最重要的是,根据我的经验,删除未使用的&#39;变量)将被禁用。

但是,如果我将变量标记为volatile,我会关注阻止此范围中的优化。如果我将变量传递给函数,我通常很乐意在该函数中应用标准优化。*

即使编译器想要从函数中删除变量(我通常试图避免的优化)也是如此,即使它这样做,它也不会影响我在< em>此范围; (函数的结果)本身就是我感兴趣的序列点(和左值)。

那么,为什么放弃关于函数调用的限定符会发出警告,因为它不会在当前作用域中启用重新排序?这是因为在被调用函数的范围内可能重新排序,volatile变量是不允许的?如果是这样,为什么这是当前范围的问题?

(*这通常是因为这样的调用用于启动异步操作,最终将对传递给函数的指针进行操作。该函数可以使用指针执行任何操作,只要它最终根据请求更新它。 volatile限定符用于提醒编译器本地变量将异步更改。)

3 个答案:

答案 0 :(得分:21)

这里的警告是因为编译器假定当你有一个指向volatile指针对象的指针时,你真的相信指针值可能会从外部源改变。当您将此指针传递给要求指向非volatile对象的指针的函数时,编译器会警告您,函数调用可能会以不正确地解释对象这一事实的方式进行优化可能会改变。

事实上,你肯定知道这样做是可以的,这意味着你可能希望放入一个删除volatile的显式强制转换,例如:{/ p>

awesome_function((uint8_t*) &thingy);

这明确告诉编译器“我知道我在这里删除volatile,所以不要警告我。”毕竟,警告的重点是你可能没有注意到这一点。

一个好的模拟是考虑const。如果你有一个指向const对象的指针,你承诺不通过指针修改该对象。如果您尝试将此指针传递给一个带有指向非const对象的指针的函数,则会收到警告,因为编译器会注意到您可能会意外地通过该函数更改该值。放入一个显式的强制转换将是一种告诉编译器的方法“是的,我知道这个指针不应该被用来修改东西,但我保证我知道我在做什么。”

希望这有帮助!

答案 1 :(得分:2)

volatile原因不是为了阻止优化。这是完成到易失性变量的事情之一,但原因是向编译器指示变量可能在C“虚拟控制之外发生变化机器“,所以它不应该对它做太多假设。

为此,它是变量本身的属性,而不是所述变量的范围。

如果一个变量是volatile,那么传递一个指向该变量的指针并不会让它变得非易失性,它只会给你一个volatile变量的地址。这是应该的。

如果您只想根据变量的范围选择优化,volatile不是作业的工具。您必须找到一些其他(可能是非标准的)方式,例如在使用#pragma warning的演员阵容期间禁用该特定警告 - 这当然取决于您的环境。

答案 2 :(得分:2)

volatile最初是为了防止对I / O读/写进行优化或重新排序。

例如,如果循环将字节序列写入内存映射的UART发送寄存器,则默认情况下,编译器将其视为具有指定大小的内存位置,就像其他任何位置一样:

  • 如果这些写操作不去I / O寄存器,通常(优化)优化循环并将复制到该内存位置是非常有意义的。或者,如果该位置没有外部可见性或读取访问权限,请完全优化写操作。
  • 但是,由于该 I / O寄存器,为了正确操作,要求每个字节被写入并且以原始顺序发生。将"Test!"的串行输出“优化”为"!"或重新排序为"!tseT"并没有帮助。

另一个例子:在“等到时间X”功能中读取实时时钟寄存器。如果允许编译器优化多次读取,则它可以一次将RTC时间值读入寄存器,然后对您的waitUntilTime()函数来说永远是。下午1:00:04。

有关更多信息,请参见https://en.cppreference.com/w/cpp/language/cvhttps://en.cppreference.com/w/c/language/volatile