volatile 有什么作用?

69 阅读2分钟

前置知识

编译器的优化

XBYTE[2]=0x55;``XBYTE[2]=0x56;``XBYTE[2]=0x57;``XBYTE[2]=0x58;

对外部硬件而言,上述四条语句分别表示不同的操作,会产生四种不同的动作,但是编译器却会对上述四条语句进行优化,认为只有XBYTE[2]=0x58(即忽略前三条语句,只产生一条机器代码)。如果键入volatile,则编译器会逐一地进行编译并产生相应的机器代码(产生四条代码)

也就是说编译器优化可能会导致我们不愿意看到的结果

一个示例

#include<stdio.h>
#include<signal.h>
   int flag=0;
void handler()
{
    flag=1;
    printf("进程被发送了信号\n");
}

int main()
{
    
    signal(2,handler);
    while(!flag)
    {
        printf("进程正在持续\n");
    }
    printf("进程正常退出");
}

在这一段代码中,程序在前台运行的时候,我们对程序输入2号信号,按道理这个程序会结束,然而事实却不是这样信号被收到了,flag虽然被改变了但是进程仍然持续,这就是编译器优化的结果,编译器优化使falg的值被放进寄存器里面没有进行更新,这和内存中的flag并不一样

这样volatile就应运而生,加入这个关键字,会确保这条指令不会因为编译器的优化而忽略,且要求每次从内存中读值

#include<stdio.h>
#include<signal.h>
  volatile int flag=0;
void handler()
{
    flag=1;
    printf("进程被发送了信号\n");
}

int main()
{
    
    signal(2,handler);
    while(!flag)
    {
        printf("进程正在持续\n");
    }
    printf("进程正常退出");
}

image.png

什么时候用voatile

1、中断服务程序中修改的供其它程序检测的变量需要加volatile;

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

3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能有不同意义;

另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实现,2 中可以禁止任务调度,3中则只能依靠硬件的良好设计了。