15

(在我开始之前:我知道关于这个主题存在现有 问题,但我没有找到答案为什么这是一个问题。我经常这样做,并想知道我是否正在制造潜在的问题。)

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

情况如下:

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

awesome_function(&thingy); << warning

现在,我的理解是,volatile限定符将变量标记为可能以编译器无法控制的方式发生变化的变量。某些优化(最重要的是,根据我的经验,删除“未使用”变量)因此被禁用。

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

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

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

(* 这通常是因为这样的调用用于启动异步操作,最终将在传递给函数的指针上进行操作。该函数可以对指针做任何它喜欢的事情,只要它最终按要求更新它。volatile限定符就在那里提醒编译器局部变量将异步更改。)

4

3 回答 3

30

这里的警告是因为编译器假定当您拥有指向volatile指针对象的指针时,您真诚地相信指针值可能会从外部源更改。当您将此指针传递给请求指向非volatile对象的指针的函数时,编译器会警告您该函数调用可能会以无法正确考虑对象可能更改的事实的方式进行优化。

您肯定知道这样做是可以的这一事实意味着您可能想要放入一个可移除的显式强制转换volatile,例如这个:

awesome_function((uint8_t*) &thingy);

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

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

希望这可以帮助!

于 2013-04-15T01:05:03.060 回答
3

原因volatile不是为了防止优化。这是对 volatile 变量可能会做的事情之一,但原因是向编译器表明该变量可能会在 C“虚拟机”的控制之外发生变化,因此它不应该对它做出太多假设.

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

如果一个变量是易失的,传递一个指向该变量的指针并不会神奇地使它成为非易失的,它只是给你易失变量的地址。这是应该的。

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

于 2013-04-15T01:05:11.137 回答
2

volatile最初旨在防止优化或重新排序 I/O 读/写。

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

  • 如果这些写入不进入 I/O 寄存器,那么(通常)优化循环并将最后一个字节复制到该内存位置会非常有意义。或者,如果该位置没有外部可见性或读取访问权限,则完全优化写入。
  • 然而,由于这一个 I/O 寄存器,为了正确操作,需要写入每个字节并且写入按原始顺序进行。"Test!"“优化” to的串行输出"!"或重新排序 to不会有帮助"!tseT"

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

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

于 2018-06-26T22:03:56.913 回答