笔记之编译器优化(volatile关键字)问题

一、碰到的问题

前段时间在编写代码的时候,碰到这个问题,我在中断中对变量赋值 RE_Flag=1,然后在其他的一个任务中执行这行代码while(RE_Flag==0){},按照我的理解:这个任务中会等待RE_Flag=1时,然后才会执行后面的代码,但是我调试发现,程序一直卡在 while(RE_Flag ==0){}这行代码,但是RE_Flag这个变量已经变成了1,但是为什么一直卡在while这呢?

二、变量关键字

用volatile关键字是防止变量被编译器优化
volatile 是在C ,C++,Java等中语言中的一种修饰关键字。
这个关键字在嵌入式系统中,是一个非常重要的一个使用。但是在单片机中,如果不熟悉这个关键字,很有可能产生想像不到的意外。就像我上面的现象…
关于volatile的意义,根据标准C的定义:
volatile的目的是,避免进行默认的优化处理.比如说对于编译器优化的功能,如果从编译器看来,有些多余的代码的话,编译器就会启动优化程序,并删除一些代码,但是这在嵌入式系统中很有可能是关键性的处理,必须不能保证被编译器删掉,所以提供了Volitile来声明,告诉编译器无论如何都不要删掉我。
像我上面的代码:

uint8_t flag;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)//中断
{
	if(GPIO_Pin == A7106_IRQ_Pin)
	{
		flag=1;
	}
}

void function(void//任务函数
{
	....
	while(RE_Flag ==0){}
	.....
}

此时没有在任务中改变这里的RE_Flag 的值,这样的话,RE_Flag 看起来就像是多余的,因此单片机编译器可能把此程序看为下段程序

uint8_t flag;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)//中断
{
	if(GPIO_Pin == A7106_IRQ_Pin)
	{
		flag=1;
	}
}

void function(void//任务函数
{
	....
	if(RE_Flag==0)
	{
		while(1){...}
	} 
	.....
}

对于一般的编译器,一般都会把程序优化成上述程序。 优化while循环使其不必每次判断条件
这样的优化确实可以提高代码速度,但是我的本意是想等待他为1就跳出循环,这样优化的话,那么自然不会跳出循环。
为了避免这种情况,我们使用volatile关键字来防止程序被编译器优化,只要在变量前增加关键字即可:

volatile uint8_t flag;

这位博主讲解的很不错:https://blog.csdn.net/fengyunjh6/article/details/9055359

三、volatile关键字的使用

这里推荐一篇知乎上详解:https://zhuanlan.zhihu.com/p/24402180
详细的介绍了 ARM C语言编程优化策略(KEIL平台)

没有使用 volatile 修饰变量时:

int buffer_full;
int read_stream(void)
{
    int count = 0;
    while (!buffer_full)
    {
        count++;
    }
    return count;
}

-O2 优化等级其汇编代码为:

read_stream PROC
LDR r1, |L1.28|
MOV r0, #0
LDR r1, [r1, #0]
|L1.12|
CMP r1, #0
ADDEQ r0, r0, #1
BEQ |L1.12| ; infinite loop
BX lr
ENDP
|L1.28|
DCD ||.data||
AREA ||.data||, DATA, ALIGN=2
buffer_full
DCD 0x00000000

使用 volatile 修饰 buffer_full 时,-O2下汇编代码为:

read_stream PROC
LDR r1, |L1.28|
MOV r0, #0
|L1.8|
LDR r2, [r1, #0]; ; buffer_full
CMP r2, #0
ADDEQ r0, r0, #1
BEQ |L1.8|
BX lr
ENDP
|L1.28|
DCD ||.data||
AREA ||.data||, DATA, ALIGN=2
buffer_full
DCD 0x00000000

可以看到,没有使用 volatile 时,while 循环查询的变量被优化了(只查询一次变量,并把它存在寄存器中,循环结束条件判断直接和保存变量值的寄存器进行比较,而不再更新寄存器的值),而 volatile 修饰后,则每次循环都查询(可以在汇编中看到,每一次循环体执行开始,首先会加载变量值到寄存器中)。
特别是在中断、多线程及寄存器读取中一定要注意使用 volatile 修饰易变的变量。

四、keil中优化等级

在这里插入图片描述
这是keil中优先等级的设置
-O0:最少的优化,可以最大程度上配合产生代码调试信息,可以在任何代码行打断点,特别是死代码处。

-O1:有限的优化,去除无用的inline和无用的static函数、死代码消除等,在影响到调试信息的地方均不进行优化。在适当的代码体积和充分的调试之间平衡,代码编写阶段最常用的优化等级。

-O2:高度优化,调试信息不友好,有可能会修改代码和函数调用执行流程,自动对函数进行内联等。

-O3:最大程度优化,产生极少量的调试信息。会进行更多代码优化,例如循环展开,更激进的函数内联等。

本文主要为记录平常编写代码的错误问题,如有侵权和错误之处,请联系作者,谢谢!
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值