在嵌入式面试时volatile是必考题!
下面来解释一下volatile关键字的作用:
volatile关键字,是用于表明变量代码无法被优化!
比如:
int a = 0;
a = 1;
a = 2;
a = 3;
经过编译器代码优化后:
int a = 0;
a = 3;
省去重复工作,debug下不会作任何优化,但这样的代码效率一般只用在调试下,release模式下会对齐进行优化,在GCC的编译器下会直接对这段代码优化,GCC下没有debug和release模式!
这样的情况对,应用层来说可以解决重复代码,有效提高编码以及运行效率!
但是对于嵌入式开发就不一样了,每个电平对于不同的情况
GPIO_C |= (1<<5);
sleep(0xFFFFF);
GPIO_C |= (0<<5);
这段代码是让LED灯闪烁,但是倘若编译器为其进行了优化就会变成:
sleep(0xFFFFF);
GPIO_C |= (0<<5);
看不到闪烁的情况了,因为编译器觉得GPIO_C最终的结果会是 |= 0<<5,所以直接索性优化掉上面重复代码!
这样的话GPIO_C端口就接受不到高电平,也就无法驱动LED灯亮起,所以这样做是不对的!
所以我们要用到volatile关键,告诉编译器,无论如何都不要尝试对被volatile关键字声明的变量进行优化!
这样的话编译器就不会对齐变量进行优化!
volatile int a = 0;
即使是宏的情况下,编译器也一样会对其进行优化,所以我们要在宏定义里也加上volatile:
#define volatile *(unsigned int*) 0x410000
这里在说说宏定义:
#define BLOCK_2_GPIOB_CRL volatile *(unsigned int*)BLOCK_2_APB2_GPIO_B+0x0 //BLOCK_2_APB2_GPIO_B = 0X100
在运用时
BLOCK_2_GPIOB_CRL |= 1
编译时会出现错误:表达式必须为左值
其实原因很简单,下面给大家看一下预编译文件:
volatile *(unsigned int*)0x100+0x0 |= 1
后面的0x100+0x0就错了!
编译器看成先对0x100地址取值,然后在加上0x0|=1
这样显然是错误的,0x0是立即数,也称为右值,无法赋值的!
所以我们要加上括号增加运算符优先级
volatile *(unsigned int*)(BLOCK_2_APB2_GPIO_B+0x0)
预编译后:
volatile *(unsigned int*)(0x100+0x0) |= 1
这样编译器就会把0x100+0x0的地址里的值|=1
也就是将0x100这块内存地址的值赋予新的值,新值是0x100里的值|=1
因为上面用了赋值运算符=