2

笔记:

这与被问过很多次的事情不一样。是的,我已经阅读了许多关于铸造无效的帖子。这些问题都没有导致我怀疑这里的答案是正确的。


背景资料:

嵌入式 C。这与内存映射的易失性指针特别相关。换句话说,外围寄存器。

我在涉及写入 I2C 外围设备的例程中遇到了以下行:

(void) I2C1->SR2;

I2C1被 #定义为 struct * 到易失性内存。

所以这一行的结果不是“避免编译器警告”,就像我在这里所做的所有搜索的答案一样。它实际上导致编译器读取该寄存器(因为它是易失性的)然后将其丢弃。该寄存器中有标志。读取寄存器的行为会导致标志清零。

现在这非常重要,因为目标是清除标志,而不仅仅是避免一些编译器警告!

然而,让我担心的是,在某种程度的优化或不同的编译器上,这段代码会被优化掉。那是我的问题:

这会被优化掉还是有办法保证它不会被优化掉?

我将所有相关代码放在下面:

#define PERIPH_BASE           ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region                                */
#define APB1PERIPH_BASE       PERIPH_BASE
#define I2C1_BASE             (APB1PERIPH_BASE + 0x5400)
#define I2C1                ((I2C_TypeDef *) I2C1_BASE)
typedef struct
{
  __IO uint16_t CR1;        /*!< I2C Control register 1,     Address offset: 0x00 */
  uint16_t      RESERVED0;  /*!< Reserved, 0x02                                   */
  __IO uint16_t CR2;        /*!< I2C Control register 2,     Address offset: 0x04 */
  uint16_t      RESERVED1;  /*!< Reserved, 0x06                                   */
  __IO uint16_t OAR1;       /*!< I2C Own address register 1, Address offset: 0x08 */
  uint16_t      RESERVED2;  /*!< Reserved, 0x0A                                   */
  __IO uint16_t OAR2;       /*!< I2C Own address register 2, Address offset: 0x0C */
  uint16_t      RESERVED3;  /*!< Reserved, 0x0E                                   */
  __IO uint16_t DR;         /*!< I2C Data register,          Address offset: 0x10 */
  uint16_t      RESERVED4;  /*!< Reserved, 0x12                                   */
  __IO uint16_t SR1;        /*!< I2C Status register 1,      Address offset: 0x14 */
  uint16_t      RESERVED5;  /*!< Reserved, 0x16                                   */
  __IO uint16_t SR2;        /*!< I2C Status register 2,      Address offset: 0x18 */
  uint16_t      RESERVED6;  /*!< Reserved, 0x1A                                   */
  __IO uint16_t CCR;        /*!< I2C Clock control register, Address offset: 0x1C */
  uint16_t      RESERVED7;  /*!< Reserved, 0x1E                                   */
  __IO uint16_t TRISE;      /*!< I2C TRISE register,         Address offset: 0x20 */
  uint16_t      RESERVED8;  /*!< Reserved, 0x22                                   */
  __IO uint16_t FLTR;       /*!< I2C FLTR register,          Address offset: 0x24 */
  uint16_t      RESERVED9;  /*!< Reserved, 0x26                                   */
} I2C_TypeDef;

在某个函数中的某个地方....

(void) I2C1->SR2;

提前感谢您的帮助。对于像我这样的新手来说,这个网站是一个很好的资源。

4

1 回答 1

3

volatile关键字是防止内存访问被优化和/或重新排序的可移植方式。应该注意的是,正确使用volatile关键字会使表达式的结果转换为(void)不必要的。例如,假设我已经定义了一个结构并拥有该结构的一个实例。

typedef struct 
{
    int a;
    int b;
}
    SomeStruct;

SomeStruct test;

以下代码会导致编译器报错,“警告:未使用的表达式结果”

    SomeStruct *vptr = &test;
    vptr->a;

我可以通过将结果转换为 来避免警告(void),但是编译器可以自由地优化读取。

    SomeStruct *vptr = &test;
    (void) vptr->a;

但是,如果我将指针声明为volatile并且不强制转换为(void),我将不会收到警告,编译器也不会优化读取。

    volatile SomeStruct *vptr = &test;
    vptr->a;

这个故事的寓意是,如果您使用volatile关键字,则不应将表达式转换为(void). 这只会抑制警告,否则会识别volatile关键字的缺失或错误使用。

于 2014-05-19T23:48:18.407 回答