C语言Const、volatile等关键字的使用

参考知乎文章【嵌入式中C语言volatile和const的作用】https://zhuanlan.zhihu.com/p/27484194
常常与硬件打交道的嵌入式C语言编程中,由于其硬件特性,某些变量的值不仅仅在软件中改变,由于硬件的状态也会使其发生变化,如某个状态寄存器。

1.latile关键字的意义:

volatile本意:不稳定的;易发散的;易变的,无定性的。
在嵌入式环境中用volatile关键字声明的变量,在每次对其值进行引用的时候都会从原始地址取值,而不会将值保存在栈或其他位置。也是在告诉编译器,针对该值不要做优化。例如:

1.1某个寄存器值做一次翻转,LED亮灭:

uint8_t *addr =...; //寄存器地址 
*addr = 1; //第一个指令,寄存器值置高
*addr = 0; //第二个指令,寄存器值置低

我们设想对它做一次翻转,但编译器可能会对它优化成这样:

uint8_t *addr =...; //寄存器地址 
*addr = 0; //第二个指令,寄存器值置低

这里的第一个指令被优化掉了,没有达成我们想要的目标。

正确做法是加上volatile关键字,对于IO寄存器都应该加上volatile

volatile  uint8_t *addr =...; //寄存器地址 
*addr = 1; //第一个指令,寄存器值置高
*addr = 0; //第二个指令,寄存器值置低

1.2变量值可能在程序外发生改变,用volatile定义的变量会在程序外被改变,每次都必须从内存中读取,而不能重复使用放在寄存器或栈中的备份。

volatile uint8_t flag;
flag=0;
while(!flag){
 doSomeThing();
}
doSomeThingElse();

如果没有volatile修饰flag,可能由于编译器优化,一直在while循环中, doSomeThingElse()不会被执行(即使flag的值在其他中断中被更改)。若如上使用了volatile,则会每次都会从原始地址取值,这样当原始地址的值更改后则while能中止并继续执行下方代码。且多任务环境下各任务间共享的标志也应该加volatile。

1.3存储器映射的硬件寄存器通常也要加voliate,因为每次对它的读写都可能有不同意义。

int *uartSendRegist= (unsigned  int *)0xXXXXXXXXX;//定义一个串口数据寄存器地址;
int init(void)
{
   int i;
   for(i=0;i< 10;i++)
   {
     delaySomeTime();
     *uartSendRegist= data[i];
   }
}

2.Const关键字

const本意位常量,

2.1 编译器一般不为const变量分配内存,而是将它保存在ROM中中,这使得它成为一个编译期间的值,没有了存储与读内存的操作。

2.2 const在编译期保证在C的“源代码”里面,没有对其修饰的变量进行修改的地方(如有则报错,编译不通过),而运行期该变量的值是否被改变则不受const的限制。

3.同时修饰一个变量

表示一个变量在程序编译期不能被修改且不能被优化;在程序运行期,变量值可修改,但每次用到该变量的值都要从内存中读取,以防止意外错误。

#include <stdio.h>

const volatile int a = 10;

int main(void){
 // a = 100; // 非法
 int* aPtr = &a;
 *aPtr = 100;

 printf("%d,%d",a,*aPtr);
}
posted @ 2023-03-06 11:20  seekwhale13  阅读(182)  评论(0)    收藏  举报