C语言中volatile关键字详解以及常见的面试问题

Posted 代二毛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言中volatile关键字详解以及常见的面试问题相关的知识,希望对你有一定的参考价值。

编译器的优化

    程序运行的优化可以分为硬件和软件。硬件上是在CPU和内存中间增加cache,来解决CPU和内存之间运行速率差异过大的问题。软件上则分为编译器优化和程序员优化:程序员优化是程序员在编写代码时,对代码的逻辑顺序进行合理安排,提升效率;编译器优化则是程序员写好的代码,在编译链接时由编译器进行优化,会调整代码的执行顺序或者删掉一些无用的语句。
    编译器优化常用的方法有:将内存变量缓存到寄存器;调整指令顺序充分利用CPU指令流水线,常见的是重新排序读写指令。因为CPU和内存的读写速度差异过大,所以编译器会尽量减少读写内存的操作,比如把中间变量或者刚读取的数据存在cache、寄存器中。编译器有一种技术叫做数据流分析,分析程序中的变量在哪里赋值、在哪里使用、在哪里失效,分析结果可以用于常量合并,常量传播等优化,进一步可以消除一些代码,换句话说就是程序员写的代码不是都会执行的,有时候编译器会删除掉一些编译器认为无意义的代码。

C语言中volatile关键字的作用

volatile int a;
main()
{
	a=0;
	b = a;
	printf("b = %d\\n", b);
}

volatile的意思是“多变的”,被volatile修饰的变量则表示该变量会被意想不到的改变,每次使用到该变量时都要去该变量的内存地址处读取,而不要去使用暂存的该变量的值。比如上面的代码,当执行b=a语句时,编译器会判断在a赋值以后就没有改变a的值,因此会把寄存器中暂存的a的值给b,而不是去a的内存地址处去读取a再赋值给b。一般情况这样是没问题的,但是有中断程序或者别的进程去修改了a的值,那寄存器里暂存的a就和内存里存放的a不一致了。不加volatile修饰,编译器还是会将寄存器暂存的a赋值给b,此时寄存器里a的值已经不再是最新的a的值,赋值是有问题的;加了volatile修饰,每次使用a时都会去a的内存地址处读取a,这样能保证每次读取的a都是最新的值,但是会导致效率降低,因为读取内存的速度远低于读取寄存器的速度。简单来说:加了volatile修饰,每次使用该变量都要去内存地址处读取,程序员写的每一行代码都要执行,不要编译器做优化,因为编译器在优化时会将编译器认为无用的代码删掉。

volatile关键字的应用实例:

1.中断服务程序中修改的某个变量值是供其他程序检测的状态值

static int status;

int main(void)

{
     while (1)
     {
		if (status) 
		{
			printf("status = 1\\n");
			status = 0;
		}
	}
}
//中断服务程序
void ISR_fun(void)

{
      status=1;
}

代码解析:在上面的程序中,中断服务程序会改变status的值,目的是让主程序打印一次"status = 1\\n"。主程序并不知道会有中断服务程序去修改status的值,经过编译器优化后,主程序第一次读取status的值是从内存处读取,以后使用status值时都用的之前读取的那份;那中断服务程序就算执行并改变了status的值,主程序也不会再去内存处读取,在主程序中status的值永远不会变,这样程序就出错了。如果加上volatile修饰status变量,那主程序每次使用status值时都会去内存地址处读取,这样就能保证中断服务程序修改status的值后,能真正的起到作用。

2.操作硬件设备的某些寄存器时需要加volatile修饰:

volatile int  *sequenceInit = (unsigned  int *)0xfffff000;//定义一个寄存器操作指针

int   init(void)

{
      int i;
      for(i=0;i< 10;i++)
      {
         *sequenceInit = i;
	}
}

代码解析:ARM芯片是统一编址,操作寄存器就和读取内存地址是一样的,假设上面的程序是ARM芯片对内存的初始化代码。ARM芯片初始化内存就是通过操作内存控制器去初始化内存,按照数据手册的初始化说明向内存控制器的寄存器依次写入特定值。假设for循环是时序的初始化,要依次向地址为0xfffff000的寄存器写入0到9。如果不加volatile修饰,编译器会认为依次向寄存器写入0到9和直接写入9效果是一样的,那整个for循环会被替换成sequenceInit =9,显然这样是不能初始化内存的,初始化会失败。以上分析可知,此时的sequenceInit 必须加volatile修饰。

volatile常见的面试问题:

1.一个参数既可以是const还可以是volatile吗?

可以的,例如只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

2.一个指针可以是volatile 吗?

可以,当一个中服务子程序修该一个指向一个buffer的指针时。指针是一种普通的变量,从访问上没有什么不同于其他变量的特性。其保存的数值是个整形数据,和整型变量不同的是,这个整型数据指向的是一段内存地址。

以上是关于C语言中volatile关键字详解以及常见的面试问题的主要内容,如果未能解决你的问题,请参考以下文章

C/C++中volatile关键字详解 (转)

详解C中volatile关键字

C语言中的常见关键字

C语言中的常见关键字

volatile关键字详解

C/C++面试必备详解C/C++中volatile关键字