Приведение результатов изменчивого выражения в void
Замечания:
Это не то же самое, что спрашивали много раз. Да, я прочитал много много постов о том, чтобы бросить в void. Ни один из этих вопросов не привел к ответу, который я подозреваю, является правдой здесь.
Справочная информация:
Встроенный C. Это особенно относится к энергозависимым указателям, отображаемым в памяти. Другими словами, периферийные регистры.
Я наткнулся на следующую строку в процедуре, которая включает в себя запись на периферийное устройство I2C:
(void) I2C1->SR2;
I2C1
# определяется как структура * для энергозависимой памяти.
Таким образом, результатом этой строки является НЕ "избегать предупреждения компилятора", как и ответ на все поиски, которые я сделал здесь. Фактически это вызывает компилятор читать этот регистр (так как он изменчив), а затем выбрасывать его. В этом регистре есть флаги. Чтение регистра приводит к очистке флагов.
Теперь это очень важно, поскольку цель состояла в том, чтобы очистить флаги, а не просто избежать предупреждения компилятора!
Однако меня беспокоит то, что на каком-то уровне оптимизации или, возможно, другом компиляторе этот код будет оптимизирован. Это мой вопрос:
Будет ли это оптимизировано или есть способ гарантировать, что он не будет оптимизирован?
Я собрал весь соответствующий код ниже:
#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;
Заранее благодарю за любую помощь. Этот сайт был отличным ресурсом для таких новичков, как я.
1 ответ
volatile
Ключевое слово - это переносной способ предотвращения оптимизации доступа к памяти и / или ее переупорядочения. Следует отметить, что правильное использование volatile
Ключевое слово делает приведение результатов выражения к (void)
ненужным. Например, допустим, у меня есть typedef'd структура и есть экземпляр этой структуры.
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
ключевое слово.