问题引出
在嵌入式开发中,对于指向寄存器的指针,前面常常加上修饰符volatile
,比如下面的代码:
static volatile unsigned int *CCM_CCGR1;
- 1
这个指针指向IMX6ULL的寄存器CCM_CCGR1
,前面用了修饰符volatile
,下面是详细解读。
volatile
关键字概述
在C语言中,关键字 volatile
是一种类型修饰符,表示所修饰的变量可能在程序的控制流之外被修改,通常用于硬件寄存器、信号处理程序或者多线程的共享变量等场景。它告诉编译器不要对该变量的读写操作进行优化。
在上下文中的意义:
在代码中,CCM_CCGR1
是指向硬件寄存器的指针。这些指针指向的寄存器的值可能在程序之外由硬件修改,比如时钟控制或其他外设配置。通过添加 volatile
修饰符,可以确保每次访问该变量时,程序都会重新从寄存器地址读取值,而不是使用编译器可能优化存储的缓存值。
volatile
的关键作用:
-
防止编译器优化:
编译器通常会优化代码,将变量存储在寄存器中而不是每次都访问内存。加了volatile
后,编译器不会对该变量进行此类优化,每次都从变量所在的实际地址读取或写入值。 -
确保数据的即时性:
如果变量的值是由外部事件(如硬件、其他线程)改变的,加上volatile
后,编译器会确保对该变量的访问始终获取最新的值。 -
典型应用场景:
- 硬件寄存器: 像代码中的
CCM_CCGR1
,其值是由硬件控制,可能随时改变。 - 中断处理程序: 中断可能会修改某些全局变量。
- 多线程环境: 多线程间共享的变量可能会被其他线程修改。
- 硬件寄存器: 像代码中的
示例说明:
int value = 0; // 非volatile变量
volatile int flag = 0; // volatile变量
void func(void)
{
while (!flag) // 如果没有volatile,编译器可能优化为死循环
{
// 编译器可能认为flag不会被修改,而优化掉重复读取
}
// 当flag被硬件或其他程序修改后,带volatile的flag能反映最新值
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
在代码中的意义
static volatile unsigned int *CCM_CCGR1;
- 1
表示 CCM_CCGR1
是一个指向硬件寄存器的指针,由于硬件寄存器的值可能随时发生变化,使用 volatile
确保每次访问时都读取最新值。
特别注意:volatile的位置不一样,意义也不一样
volatile
修饰的不同位置会对代码的含义产生不同影响:
-
修饰指针指向的内容:
volatile unsigned int *ptr;
- 1
表示
ptr
所指向的地址(寄存器的值)是易变的,每次访问时都会重新读取该地址的值。 -
修饰指针变量:
unsigned int *volatile ptr;
- 1
表示
ptr
这个指针变量本身是易变的,编译器不能优化对ptr
本身的访问,每次都要重新读取指针的值。
在这个地方,显然指针所指向的内容是易变的,而指针变量本身不是易变的,所以volatile
的位置在前方,所以要用1
中的代码。



评论记录:
回复评论: