编译器优化、volatile关键字

编译器的优化:

程序运行的优化可以分为硬件和软件。

硬件:在CPU和内存中间增加缓存区 (cache)来解决CPU和内存之间运行速率差异过大的问题。
软件上则分为 编译器优化和程序员优化:
程序员优化: 程序员对程序算法、逻辑顺序进行合理安排。

程序员优化是程序员在编写代码时,对代码的算法、逻辑顺序进行合理安排,提升效率;

编译器优化: 编译器编译时会调整代码的执行顺序或者删掉一些无用的语句。

编译器优化则是程序员写好的代码,在编译链接时由编译器进行优化,会调整代码的执行顺序或者删掉一些无用的语句。
编译器优化常用的方法有:
将内存变量缓存到寄存器;
调整指令顺序充分利用CPU指令流水线,常见的是重新排序读写指令。
编译器会尽量减少读写内存的操作,比如把中间变量或者刚读取的数据存在cache、寄存器中。

编译器有一种技术叫做数据流分析,分析程序中的变量在哪里赋值、在哪里使用、在哪里失效,分析结果可以用于常量合并,常量传播等优化,进一步可以消除一些代码,
简而言之就是程序里写的代码不是都会执行的,有时候编译器会删除掉一些编译器认为无意义的代码

volatile 易变的

volatile和const完全相反。但是也可以同时使用:
volatile修饰表示它可能被意想不到地改变。
const修饰表示程序不应该试图去修改它。
volatile表明该变量可能会通过某种方式发生改变,
而这种方式是你通过分析正常的程序流程完全预测不出来的,使程序用到这个变量时必须每次重新读取他的值。
加了volatile修饰,每次使用该变量都要去内存地址处读取,程序写的代码都要执行,不要编译器做优化,因为编译器在优化时会将编译器认为无用的代码删掉。
如:

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

比如上面的代码,当执行b=a语句时,编译器会判断在a赋值以后就没有改变a的值,
因此会把寄存器中暂存的a的值给b,而不是去a的内存地址处去读取 a再赋值给b。
一般情况这样是没问题的,但是当有中断程序或者别的进程去修改了a的值,那寄存器里暂存的a就和内存里存放的a不一致了。
不加volatile修饰,编译器还是会将寄存器暂存的a赋值给b,此时寄存器里a的值已经不再是最新的a的值,赋值是有问题的;

加了volatile修饰,每次使用a时都会去a的内存地址处读取a,这样能保证每次读取的a都是最新的值,但是会导致效率降低,因为读取内存的速度远低于读取寄存器的速度。

应用实例1.中断服务程序中修改的某个变量值是供其他程序检测的状态值
volatile int status;
int main(void)
{
     while (1){
		if (status) {
			printf("status = 1\n");
			status = 0;
		}
	}
}
//中断服务程序
void ISR_fun(void)
{
      status=1;
}

代码解析:
在上面的程序中,加上volatile修饰status变量,那主程序每次使用status值时都会去内存地址处读取,这样就能保证中断服务程序修改status的值后,能真正的起到作用
中断服务程序会改变status的值,代码目的是让 主程序打印一次" status = 1\n"。但是主程序并不知道会不会有中断服务程序去修改status的值
不加volatile修饰,经过编译器优化后,主程序第一次读取status的值是从内存处读取,之后使用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芯片初始化内存就是通过操作内存控制器去初始化内存,
按照数据手册的初始化说明 向内存控制器的寄存器依次写入特定值。
假设for循环是时序的初始化,要依次向地址为0xfffff000的寄存器写入0到9。
如果不加volatile修饰,编译器认为依次向寄存器 写入0到9 和 直接写入9 效果是一样的,那整个for循环会被替换成sequenceInit =9,显然这样是不能初始化内存的,初始化会失败
以上分析可知,此时的sequenceInit 必须加volatile修饰

volatile常问问题:

1.一个参数既可以是const还可以是volatile吗?
可以的,例如只读的状态寄存器。
使用volatile修饰因为它可能被意想不到地改变。
使用const修饰因为程序不应该试图去修改它。

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

原文链接:https://blog.csdn.net/weixin_42031299/article/details/115843058

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程一时爽Cxx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值