问题
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:
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.
来源:https://stackoverflow.com/questions/23746870/casting-the-results-of-a-volatile-expression-to-void