Casting the results of a volatile expression to vo

2019-08-11 21:55发布

Note:

This is Not the same thing that has been asked many times. Yes I have read the many many posts about casting to void. None of those questions resulted in the answer I suspect is true here.


Background info:

Embedded C. This is specifically related to memory mapped volatile pointers. In other words, peripheral registers.

I came across the following line in a routine that involves writing to an I2C peripheral:

(void) I2C1->SR2;

I2C1 is #defined as a struct * to volatile memory.

So the result of this line is NOT to "avoid a compiler warning" as is the answer on all the searches I did here. It is in fact causing the compiler to read that register (since it's volatile) and then throw it away. This register has flags in it. The act of reading the register causes the flags to clear.

Now this is pretty important since the goal was to clear the flags not just avoid some compiler warning!

What has me worried however, is that at some level of optimization or maybe a different compiler, this code will get optimized away. That is my question:

Will this get optimized away or is there a way to guarantee it won't be optimized away?

I put all the relevant code together below:

#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;

Somewhere down in a function....

(void) I2C1->SR2;

Thanks in advance for any help. This site has been a great resource for newbies like me.

1条回答
倾城 Initia
2楼-- · 2019-08-11 22:10

The volatile keyword is the portable way to prevent memory accesses from being optimized away and/or reordered. It should be noted that the proper use of the volatile keyword makes casting the results of the expression to (void) unnecessary. For example, let's say I've typedef'd a structure and have an instance of that structure.

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

SomeStruct test;

The following code will cause the compiler to complain, "warning: expression result unused"

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

I can avoid the warning by casting the result to (void), but then the compiler is free to optimize away the read.

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

However, if I declare the pointer as volatile and don't cast to (void), I won't get a warning and the compiler will not optimize away the read.

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

The moral of the story is that if you are using the volatile keyword, you should not cast expressions to (void). That will only suppress warnings that would otherwise identify missing or incorrect use of the volatile keyword.

查看更多
登录 后发表回答