0

我的代码是用 C 编写的。我有一个 ISR(中断服务例程),它使用全局变量与主代码进行通信。ISR 与主代码位于不同的编译单元中。有什么理由我不能对主代码使用“易失性”,而是将其留在 ISR 中吗?

我的推理如下: volatile 限定符阻止编译器完全优化 ISR。从 ISR 的角度来看,该变量不是易失的 - 即在 ISR 期间它不能被外部更改,并且在 ISR 期间不需要输出值。此外,如果 ISR 在其自己的编译单元中,编译器必须让 ISR 在第一次使用之前从内存中读取全局,并且必须在返回之前将更改存储回来。我的理由是:不需要同时编译不同的编译单元,因此编译器不知道 ISR 范围之外发生了什么(或者它应该假装),因此它必须确保读取全局/写在 ISR 的边界。

也许,我误解了编译单元的意义?我发现的一个参考资料说 GCC 使这种易失性不匹配成为编译时错误;我不确定它怎么可能,如果它们在不同的编译单元中,它们不应该是独立的吗?我可以不单独编译库函数并稍后链接它吗?

使用 volatile 破坏系统代码的九种方法

也许可以从序列点的概念提出一个论据。我不完全理解序列点或副作用的概念;但是,C99 规范在 5.1.2.3 第 2 段中指出:“......在称为序列点的执行序列中的某些指定点处,先前评估的所有副作用都应是完整的,并且不应发生后续评估的副作用。 "

附件 C,列出了包括以下内容的序列点:

  • 在评估参数之后对函数的调用。
  • 紧接在库函数返回之前。

参考:WG14 文件:N1013,日期:2003 年 5 月 7 日

注意:上一个问题,相对于函数调用和返回的全局变量访问询问是否在函数调用和返回之前/之后存储/写入全局变量。但这是一个不同的问题,它询问全局变量在不同的编译单元中是否可能被不同地限定为“易失性”。我使用了很多相同的推理来证明我的初步结论是正确的,这促使一些读者认为这是同一个问题。

4

1 回答 1

0

ISO/IEC 9899:2011(C11 标准)说:

6.7.3 类型限定符

¶6 如果尝试通过使用具有非 const 限定类型的左值来修改使用 const 限定类型定义的对象,则行为未定义。如果尝试通过使用具有非 volatile 限定类型的左值来引用使用 volatile 限定类型定义的对象,则行为未定义。133)

133)这适用于那些表现得好像它们是用限定类型定义的对象,即使它们实际上从未定义为程序中的对象(例如内存映射输入/输出地址处的对象)。

¶6 的第二句话说,如果您有此处显示的任何一个组织,您就会调用未定义的行为:

File main.c                             File isr.c:

volatile int thingamyjig = 37;          extern int thingamyjig;         // V1

extern int thingamyjig;                 volatile int thingamyjig = 37;  // V2

在 V1 或 V2 的每种情况下,您都会违反标准该部分中指定的未定义行为——尽管我认为 V1 是您在问题中所描述的。

限定词volatile必须一致地应用:

File main.c                               File isr.c:

volatile int thingamyjig = 37;            extern volatile int thingamyjig;  // V3

extern volatile int thingamyjig;          volatile int thingamyjig = 37;    // V4

V3 和 V4 都一致地保留了 volatile-qualifiers。

请注意,“未定义行为”的一种有效表现形式是“它的行为正常且如您所愿”。不幸的是,这不是未定义行为的唯一或必然是最合理的可能表现形式。不要冒险。保持一致。

于 2016-09-12T00:17:09.977 回答