首页 > 编程知识 正文

c语言易变的变量的关键字,c++ volatile

时间:2023-05-04 19:09:55 阅读:148595 作者:4276

一、volatile介绍了volatile,提醒编译器后面定义的变量随时可能改变。 因此,编译后的程序每次需要保存或读取该变量时,都直接从变量地址读取数据。 如果没有volatile关键字,编译器可能会优化读取和保存,或者临时使用寄存器中的值。 如果此变量被另一个程序更新,则会发生不匹配。 让我举例说明一下。 在DSP开发中,由于需要等待某个事件的触发,所以经常编写这样的程序。

该程序等待内存变量flag的值变为1,然后执行do2 ()。 变量flag的值已被另一个程序更改。 这个程序可能是硬件中断服务程序。 例如,当某个按钮被按下时,DSP产生中断,通过用按钮中断程序将flag改为1,可以继续执行上述程序。 但是,在优化时,编译器可能会将flag的值读取到一个寄存器中,然后等待该寄存器变为1,因为编译器不知道另一个程序会更改flag的值。 如果不幸进行了这种优化,while循环将成为死循环,因为寄存器的内容不会被中断服务程序更改。 为了使程序每次都读取真正的flag变量的值,必须定义如下:

需要注意的是,没有volatile也可能正常工作,但更改编译器优化级别可能会导致无法正常工作。 因此,经常会出现调试版本正常,但版本不正常的问题。 所以为了安全起见,如果要等待另一个程序修改某个变量,就使用volatile关键字。

volatile的原意是“易变的”,因为对寄存器的访问速度超过了RAM,所以编译器通常会进行减少对外部RAM的访问的优化。 例如:

的意图是在发生ISR_2中断时在main中调用do_something函数,但由于编译器确定没有在main函数中修改I,因此只需执行一次从I到某个寄存器的读取操作。 如果对每次判断if时只使用该寄存器中的“I副本”可能不会导致do_something永远持续的变量进行volatile限定,编译器将确保该变量的读写操作不会得到优化。 在这个例子中I也应该这样解释。

通常,volatile用于以下位置:

1、需要将volatile添加到由中断服务程序修改的其他程序检测到的变量中;

2、多任务环境下各任务间共享的标志应加volatile;

3、内存映射的硬件寄存器中通常也要放入volatile的说明。 这是因为每次其读写都有可能具有不同的含义;

另外,在这些情况下,经常也需要同时考虑数据完整性,相互关联的一些标志被读一半并中断改写。 1可以通过关闭中断来实现,2可以禁止任务调度,3只能依靠硬件的优秀设计。

二、volatile 的含义volatile始终与优化相关。 编译器有一种称为数据流分析的技术,它分析解析器中的变量在何处赋值、在何处使用以及在何处禁用。 分析结果可用于常数合并、常数传播等的优化,还可消除死码。 但是,程序可能不需要这些优化。 在这种情况下,可以使用volatile关键字禁止这些优化。 volatile的字面意思容易改变,有以下作用。

1在两个操作之间不在寄存器中缓存volatile变量。 在多任务、中断甚至是setjmp环境中,变量可能会被其他程序修改,编译器自己不知道。 volatile将把这件事告诉编译器。

2不进行常数结合、常数传播等优化,因此代码如下。

if的条件不被认为是无条件真的。

不会优化对3358www.Sina.com/volatile变量的读写。 如果为变量赋值但以后不使用,编译器常常可以省略其赋值操作,但无法以这种方式优化Memory Mapped IO处理。

以前,有人说volatile可以保证对内存操作的原子性,但这个说法不太准确。 第一,x86需要LOCK前缀才能在SMP中保证原子性。 其次,RISC不能直接对内存进行运算。 要保证原子性,就必须用别的方法,比如atomic_inc。

关于jiffies,已经声明为volatile变量,所以我想直接使用jiffies就可以了。 没有必要使用那样复杂的形状。 因为那样的话原子性也不能保证。

对于奔腾和后续的CPU,您可能不知道以下两个命令的工作方式相同,但一个命令的工作速度反而比三个命令快。

3memory很特殊,可能是内嵌组件中最难理解的部分。 为了说明这一点,请介绍编译器的优化知识,然后查看C关键字volatile。 最后去看描述符。

三、编译器优化 C关键字volatile memory破坏描述符

内存访问速度远远赶不上CPU的处理速度,为了提高机器整体性能,在硬件上引入了硬件缓存Cache,以加快内存的访问速度。 另外,在现代的CPU中,命令的执行并不一定严格按照顺序执行,为了最大限度地利用CPU的命令流水线来提高执行速度,可以按顺序执行没有关联性的命令。 以上就是硬件水平的优化。 让我们来看看软件级别的优化。 一个是程序员在编写代码时进行优化,另一个是编译器进行优化。 编译器优化的常用方法是将内存变量缓存在寄存器中; 调整指令顺序要充分利用CPU指令流水线,常见的是再次

排序读写指令。对常规内存进行优化的时候,这些优化是透明的,而且效率很好。由编译器优化或者硬件重新排序引起的问题的解决办法是在从硬件(或者其他处理器)的角度看必须以特定顺序执行的操作之间设置内存屏障(memory barrier),linux 提供了一个宏解决编译器的执行顺序问题。

这个函数通知编译器插入一个内存屏障,但对硬件无效,编译后的代码会把当前CPU寄存器中的所有修改过的数值存入内存,需要这些数据的时候再重新从内存中读出。

2、C语言关键字volatile

C语言关键字volatile(注意它是用来修饰变量而不是上面介绍的volatile)表明某个变量的值可能在外部被改变,因此对这些变量的存取不能缓存到寄存器,每次使用时需要重新存取。该关键字在多线程环境下经常使用,因为在编写多线程的程序时,同一个变量可能被多个线程修改,而程序通过该变量同步各个线程,例如:

该线程启动时将intSignal置为2,然后循环等待直到intSignal为1时退出。显然intSignal的值必须在外部被改变,否则该线程不会退出。但是实际运行的时候该线程却不会退出,即使在外部将它的值改为1,看一下对应的伪汇编代码就明白了:

对于C编译器来说,它并不知道这个值会被其他线程修改。自然就把它cache在寄存器里面。记住,C 编译器是没有线程概念的!这时候就需要用到volatile。volatile 的本意是指:这个值可能会在当前线程外部被改变。也就是说,我们要在threadFunc中的intSignal前面加上volatile关键字,这时候,编译器知道该变量的值会在外部改变,因此每次访问该变量时会重新读取,所作的循环变为如下面伪码所示:

3、Memory

有了上面的知识就不难理解Memory修改描述符了,Memory描述符告知GCC:
1)不要将该段内嵌汇编指令与前面的指令重新排序;也就是在执行内嵌汇编代码之前,它前面的指令都执行完毕。
2)不要将变量缓存到寄存器,因为这段代码可能会用到内存变量,而这些内存变量会以不可预知的方式发生改变,因此GCC插入必要的代码先将缓存到寄存器的变量值写回内存,如果后面又访问这些变量,需要重新访问内存。
如果汇编指令修改了内存,但是GCC 本身却察觉不到,因为在输出部分没有描述,此时就需要在修改描述部分增加“memory”,告诉GCC 内存已经被修改,GCC 得知这个信息后,就会在这段指令之前,插入必要的指令将前面因为优化Cache 到寄存器中的变量值先写回内存,如果以后又要使用这些变量再重新读取。
使用“volatile”也可以达到这个目的,但是我们在每个变量前增加该关键字,不如使用“memory”方便。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。