C 中 volatile 非静态局部变量的原因
Reason for volatile nonstatic local variable in C
参考以下代码:
void calledFunction(volatile uint8_t **inPtr);
volatile uint8_t buffer[] = {0,0,0,0,0,0};
volatile uint8_t *headPtr = buffer;
void foo(void)
{
volatile uint8_t *tmpPtr = NULL;
tmpPtr = headPtr;
//This function modifies tmpPtr
calledFunction(&tmpPtr);
headPtr = tmpPtr;
return;
}
这是我试图使中断安全的代码的简化版本,我不确定为什么将此本地定义为 volatile
。我知道没有性能原因(即保证此函数至少为 O(n)),因为此函数 应该 运行 尽可能高效。
这个函数可以在主执行和内部中断中调用,但是由于 tmpPtr
是一个非静态局部变量,它不应该被 foo()
的任何其他实例修改。
在此上下文中我看不到任何需要 volatile
关键字的访问模式。
简而言之,函数 foo()
中 tmpPtr
的 volatile
关键字的用途是什么?
编辑:忘记函数参数中的 &
EDIT2:我继承了这段代码,需要修改。
我的主要问题是 volatile
关键字是否有任何特别有效的理由出现在这种情况下。
EDIT3:添加了 calledFunction()
的原型
EDIT4:在原始代码中添加了 重要 说明,即 headPtr
和 buffer
都有 volatile
tmpPtr
有 volatile
的原因是 tmpPtr
需要引用 volatile uint8_t
,而不是因为 tmpPtr
本身就是 volatile
(不是)。
正如@Eugene Sh. 最初指出的那样,这个问题是由于在定义 volatile
指针和变量时语法上的误解而出现的。 对指向 volatile
指针和 volatile
指针的语法有很好的解释。
Volatile 限制编译器在访问(读取或写入)此指针指向的数据时进行优化。
这经常出现在嵌入式或中断中,因为内存映射外围设备无法优化通常“无关”的读取或写入。
例如,
int32_t variable = 0;
variable = 1;
variable = 2;
variable = 3;
优化编译器会跳过将变量的值设置为 0、1 和 2,而只是将其设置为 3。这通常没问题,但如果我们不是写入普通变量,而是写入内存映射端口,我们实际上希望每个写入都发生。
这甚至可能发生在硬件接口世界之外。如果一个单独的线程在循环中寻找要设置为 2 的变量,优化编译器会阻止这种情况发生。
它是本地的事实不是 material。只是大多数 volatile 的用例都是用翻译单元(或使用 extern 的交叉翻译单元)作用域实现的。
两个例子是内存映射寄存器定义(结构通常是全局的,通常在头文件中,并且通常指向结构的指针的实例是全局的,尽管它不一定是全局的)和标志就像我们的线程示例。
我不提倡这两种设计,但您会在嵌入式开发中经常遇到它。
参考以下代码:
void calledFunction(volatile uint8_t **inPtr);
volatile uint8_t buffer[] = {0,0,0,0,0,0};
volatile uint8_t *headPtr = buffer;
void foo(void)
{
volatile uint8_t *tmpPtr = NULL;
tmpPtr = headPtr;
//This function modifies tmpPtr
calledFunction(&tmpPtr);
headPtr = tmpPtr;
return;
}
这是我试图使中断安全的代码的简化版本,我不确定为什么将此本地定义为 volatile
。我知道没有性能原因(即保证此函数至少为 O(n)),因为此函数 应该 运行 尽可能高效。
这个函数可以在主执行和内部中断中调用,但是由于 tmpPtr
是一个非静态局部变量,它不应该被 foo()
的任何其他实例修改。
在此上下文中我看不到任何需要 volatile
关键字的访问模式。
简而言之,函数 foo()
中 tmpPtr
的 volatile
关键字的用途是什么?
编辑:忘记函数参数中的 &
EDIT2:我继承了这段代码,需要修改。
我的主要问题是 volatile
关键字是否有任何特别有效的理由出现在这种情况下。
EDIT3:添加了 calledFunction()
EDIT4:在原始代码中添加了 重要 说明,即 headPtr
和 buffer
都有 volatile
tmpPtr
有 volatile
的原因是 tmpPtr
需要引用 volatile uint8_t
,而不是因为 tmpPtr
本身就是 volatile
(不是)。
正如@Eugene Sh. 最初指出的那样,这个问题是由于在定义 volatile
指针和变量时语法上的误解而出现的。 volatile
指针和 volatile
指针的语法有很好的解释。
Volatile 限制编译器在访问(读取或写入)此指针指向的数据时进行优化。
这经常出现在嵌入式或中断中,因为内存映射外围设备无法优化通常“无关”的读取或写入。
例如,
int32_t variable = 0;
variable = 1;
variable = 2;
variable = 3;
优化编译器会跳过将变量的值设置为 0、1 和 2,而只是将其设置为 3。这通常没问题,但如果我们不是写入普通变量,而是写入内存映射端口,我们实际上希望每个写入都发生。
这甚至可能发生在硬件接口世界之外。如果一个单独的线程在循环中寻找要设置为 2 的变量,优化编译器会阻止这种情况发生。
它是本地的事实不是 material。只是大多数 volatile 的用例都是用翻译单元(或使用 extern 的交叉翻译单元)作用域实现的。
两个例子是内存映射寄存器定义(结构通常是全局的,通常在头文件中,并且通常指向结构的指针的实例是全局的,尽管它不一定是全局的)和标志就像我们的线程示例。
我不提倡这两种设计,但您会在嵌入式开发中经常遇到它。