声明一个volatile变量是什么意思?


9

许多低级程序使用volatile关键字来进行内存映射的类型等,但是我对其在后台真正执行操作感到困惑。换句话说,当编译器没有“优化”内存地址时,这意味着什么?


1
如果您是从volatile变量中读取年龄并显示5,那么明年再读一次,您
肯定

@ 5gon12eder,我知道volatile意味着某些内容可以快速轻松地进行更改,但是,它如何工作?:S
Vikaton '16

另外,根据您的编译标志,非易失性变量“可能”将显示在调试器中(例如,您正在使用C代码+ Eclipse + gdb),如下所示:“优化值”,因为该变量的值现在为寄存器中的某处。如果您不知道如何使用汇编语言调试工具/功能,只需使用volatile修饰符声明变量。

Answers:


11

volatile 意味着其他一些处理器或I / O设备或某些东西可以将变量从您的下方更改。

使用普通变量,程序的步骤是唯一可以更改它的步骤。因此,举例来说,如果您5从变量中读取但未更改它,则该变量仍包含5。由于您可以依靠它,因此您的程序不必在下次要使用它时花时间再次读取该变量。C ++编译器很聪明,可以生成只记住的代码5

但是您可以将其读取为5,然后系统可能会将数据从磁盘加载到该内存中,并将其更改为500。如果您希望程序读取新的值500,则需要编译器不要对使用先前读取的内容太聪明5。您需要告诉它每次都重新加载该值。就是volatile那样

比方
5岁的孩子,假设您在一张桌子上放了一张大纸。在本文的一个角落,您写下正在进行的游戏的当前得分3 to 4。然后,您转到桌子的对面,开始写有关游戏的故事。您正在观看游戏的朋友会在游戏进行时更新该角点的分数。她擦除3 to 4并写作3 to 5

当您将游戏得分添加到故事中时,您可以:

  1. 写下您读过的最后一个乐谱3 to 4,快乐地假设它没有变化(或介意它没有变化),或
  2. 走到桌子的另一侧以读取当前乐谱(恰好是3 to 5现在),然后往回走。这就是volatile变量的作用方式。

11

volatile 意味着两件事:

  1. 变量的值可能会更改,而您的任何代码都不会更改它。因此,无论何时编译器读取该变量的值,都可能不会假定该变量与上次读取的变量相同,或假定它与存储的最后一个值相同,但必须再次读取。

  2. 从易失性变量存储值的行为是“副作用”,可以从外部观察到,因此不允许编译器删除存储值的行为。例如,如果两个值连续存储,那么编译器实际上必须将该值存储两次。

举个例子:

i = 2; 
i = i; 

编译器必须存储第二个数字,读取变量i,并将其读取的变量存储到i中。

还有另一种情况:如果某个函数使用setjmp然后longjmp被调用,则该函数的所有易失性局部变量都将保证存储了最后一个值-非易失性局部变量不是这种情况。


这里有一些细微的问题。一个微妙的问题是,您已经将易失性读取描述为变量的特征,而实际上它们是如何访问变量的特征。如果我们具有变量i和value pi = &i,则x = *pi从中进行读取i,但不能保证该读取具有易变的语义。
埃里克·利珀特

1
@EricLippert:如果i声明为,volatile int i那么pi必须声明为volatile int *pi,在这种情况下*pi是易失性访问,不是吗?
乔恩·普迪

2

抽象说明
C和C ++都具有抽象机的概念。当代码使用某个变量的值时,抽象机表示实现必须访问该变量的值。表单的代码statement_A; statement_B; statement_C;必须严格按照指定的顺序执行。这三个语句共有的表达式每次出现时都必须重新计算。

对于抽象机,给定语句序列statement_A; statement_B; statement_C;,实现必须首先statement_A完整地执行,然后再statement_B最后执行 statement_C。该实现不记得您已将age值分配为5。每个引用的语句都age必须访问该变量的值。

volatile如果实现根据抽象机规范严格执行C或C ++代码,则不需要关键字。C和C ++抽象机没有寄存器的概念,没有公共子表达式的概念,并且执行顺序严格。

两种语言也都有“如果”规则。只要该实现的行为就像已按照抽象机规范执行了所有事情,那么该实现就符合该标准。编译器可以假定非易失性变量在分配之间不会更改值。只要不违反as-if规则,就statement_A; statement_B; statement_C;可以通过执行的一部分statement_C,然后的一部分statement_A,然后的全部statement_B,然后的其余部分statement_A,最后执行的其余部分来实现该序列statement_C

这些假设规则不适用于volatile变量。关于volatile变量和函数,实现必须完全按照您告诉它的操作去做,并且必须按照您告诉它做事的顺序去做。

抽象机器规范有一个缺点:很慢。与其他语言相比,C和C ++的一个积极方面是它们非常快。如果代码是针对这些抽象机执行的,则情况并非如此。在AS-如果规则是什么让C和C ++是如此之快。

ELI5答案

编译器未“优化”内存地址是什么意思?

“优化”内存地址是一个高级概念,这不在五岁儿童的能力范围之内。顺从的五岁孩子会完全按照您告诉他们的去做,不会多也不会少。使用volatile,您将告诉实现的实现就像是五个实现:没有思考,没有花哨的优化。取而代之的是,实现必须完全执行代码告诉它的操作。


1

(非易失性)提示编译器如何优化代码(从生成的汇编代码的角度来看):

  • 非易失性意味着您当前的编译器决定变量将位于何处或将变量-s的值传递给子例程的方式
    • 在固定的内存地址中
    • 在堆栈上[相对于处理器当前的堆栈指针],
    • 在堆上[相对于处理器当前的基本指针],
    • 在处理器寄存器中
    • ...
  • volatile意味着编译器无法优化变量,因为主cpu-s控制之外的其他内容(即单独的io处理器)可以更改该值。

0

答案似乎很一致,但缺少要点。您告诉编译器您要分配空间,对于每次访问,读或写,都希望它执行该访问。由于某些原因,我们不希望它优化这些访问或该变量。

是的,原因之一是因为其他人可能会改变我们的价值。另一个原因是我们可能会为其他人更改该价值。有人是为我们更改它的人还是我们为之更改它的人可能是硬件/逻辑或软件。它通常用于定义对裸机嵌入式程序中的控制和状态寄存器的访问,以及对硬件的写入或读取。以及与软件对话的软件在其他答案中有所说明。

如果您试图定时一段代码,并且您不使用volatile,那么您还将看到volatile用于控制访问的时间和顺序,如果您不使用volatile,则仅需考虑有问题的变量(开始时间,结束时间和差值)。计算接近结束时,编译器可以随意移动任意一个时间测量值(而不是放置它们的位置),这并不是说它不能随时间变化,但是经验表明它不太可能。

有时,您会看到它过去只是用来消耗时间,一个基本的led指示灯,即裸机的世界,可能会使用volatile变量来计数,该变量计数很大,只是为了消耗时间让人眼看到led改变状态。然后,使用更高级的示例使用计时器或其他事件来消耗时间。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.