读volatile变量有持久化作用?米斯拉C
Read volatile variable has persistent effect? Misra C
阅读 Misra C 指南时,我遇到了以下示例:
extern volatile int v1, v2;
int t;
t = v1 + v2;
Per Misra C 读取变量 v1 和 v2 有一个持久的副作用。我在想,这是为什么呢?为什么读取 volatile 变量可能会影响此时的执行状态? 注意:不将总和分配给变量 t,而只是读数本身。
持久性副作用的定义:“如果副作用可能对执行产生影响,则称副作用在执行的特定时间点持续存在那个时候的状态。” [参考:Misra C,附录 J]
来自 Misra-C 标准 (Misra-C:2004),第 6 节。
The volatile type qualifier is provided in C to denote objects whose
value can change independently of the execution of the program (for
example an input register). If an object of volatile qualified type is
accessed this may change its value. C compilers will not optimise out
reads of a volatile. In addition, as far as a C program is concerned,
a read of a volatile has a side-effect (changing the value of the
volatile).
这表明表示输入寄存器的 volatile 在读取时可能会更改其值,因此读取 volatile 被认为是一种副作用。
这与 MISRA-C 本身无关,而是 C 语言本身。定义见C标准(C11 5.1.2.3):
Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment.
硬件外设标志寄存器在读取时更改其值是很常见的。例如:先读取状态寄存器再读取数据寄存器是清除 UART 或 SPI 硬件标志的常用方法。
出于这个原因,MISRA-C 希望您将可变访问隔离到简单的表达式中,这样您就不会得到意外的结果,例如 x && my_volatile
,其中 my_volatile
可能会也可能不会进行评估和更新。正确的做法是将volatile访问隔离到自己一行:
if(x)
{
int tmp = my_volatile;
// do stuff with tmp
}
这些规则还可以防止您编写功能失调的代码,例如:
REGISTER |= FLAG1;
REGISTER |= FLAG2;
...
导致对寄存器进行多次写入,这会导致执行开销和可能不需要的副作用。遗憾的是,上述错误代码场景在嵌入式固件中比您预期的要普遍得多。同样,最简单的方法是将结果存储在临时变量中并将写入隔离到一行:
uint32_t reg = FLAG1 | FLAG2 | ...;
REGISTER = reg;
阅读 Misra C 指南时,我遇到了以下示例:
extern volatile int v1, v2;
int t;
t = v1 + v2;
Per Misra C 读取变量 v1 和 v2 有一个持久的副作用。我在想,这是为什么呢?为什么读取 volatile 变量可能会影响此时的执行状态? 注意:不将总和分配给变量 t,而只是读数本身。
持久性副作用的定义:“如果副作用可能对执行产生影响,则称副作用在执行的特定时间点持续存在那个时候的状态。” [参考:Misra C,附录 J]
来自 Misra-C 标准 (Misra-C:2004),第 6 节。
The volatile type qualifier is provided in C to denote objects whose value can change independently of the execution of the program (for example an input register). If an object of volatile qualified type is accessed this may change its value. C compilers will not optimise out reads of a volatile. In addition, as far as a C program is concerned, a read of a volatile has a side-effect (changing the value of the volatile).
这表明表示输入寄存器的 volatile 在读取时可能会更改其值,因此读取 volatile 被认为是一种副作用。
这与 MISRA-C 本身无关,而是 C 语言本身。定义见C标准(C11 5.1.2.3):
Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment.
硬件外设标志寄存器在读取时更改其值是很常见的。例如:先读取状态寄存器再读取数据寄存器是清除 UART 或 SPI 硬件标志的常用方法。
出于这个原因,MISRA-C 希望您将可变访问隔离到简单的表达式中,这样您就不会得到意外的结果,例如 x && my_volatile
,其中 my_volatile
可能会也可能不会进行评估和更新。正确的做法是将volatile访问隔离到自己一行:
if(x)
{
int tmp = my_volatile;
// do stuff with tmp
}
这些规则还可以防止您编写功能失调的代码,例如:
REGISTER |= FLAG1;
REGISTER |= FLAG2;
...
导致对寄存器进行多次写入,这会导致执行开销和可能不需要的副作用。遗憾的是,上述错误代码场景在嵌入式固件中比您预期的要普遍得多。同样,最简单的方法是将结果存储在临时变量中并将写入隔离到一行:
uint32_t reg = FLAG1 | FLAG2 | ...;
REGISTER = reg;