Преобразование результатов volatile-выражения в void

Примечание.

Это не то же самое, о чем спрашивали много раз. Да, я прочитал много-много постов о переходе в пустоту. Ни один из этих вопросов не привел к ответу, который, как я подозреваю, здесь верен.


Общая информация:

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

Заранее благодарю за любую помощь. Этот сайт был отличным ресурсом для новичков, таких как я.


person user1160866    schedule 19.05.2014    source источник
comment
Обратите внимание, что без производителя/модели компилятора и процессора ни один «ответ» не может быть правильным для вашей ситуации.   -  person KevinDTimm    schedule 20.05.2014
comment
На самом деле ЭТО, я думаю, ответ на вопрос. Если это зависит от компилятора, то это неправильный способ сделать это. Итак, тот же вопрос сформулирован немного по-другому: существует ли независимый от компилятора метод для достижения того же? Кстати, компилятор GCC.   -  person user1160866    schedule 20.05.2014
comment
Если осуществляется доступ к изменчивому объекту, этот доступ не может быть оптимизирован соответствующим компилятором C.   -  person D Krueger    schedule 20.05.2014
comment
Комментарий Кевина неверен. Использование ключевого слова volatile — это портативный способ предотвратить оптимизацию доступа к памяти.   -  person user3386109    schedule 20.05.2014


Ответы (1)


Ключевое слово 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.

person user3386109    schedule 19.05.2014
comment
Я хотел бы добавить некоторые уточнения просто для протокола. - person user1160866; 20.05.2014
comment
Я хотел бы добавить некоторые уточнения просто для протокола. Это может быть неочевидно, но в приведенном выше фрагменте кода __IO — это сокращение от volatile. Кроме того, я только что скомпилировал I2C1->SR2 без приведения к void и с использованием самых высоких настроек оптимизации, и он по-прежнему правильно читает. Так что спасибо пользователю 3386109. На него был дан ответ к моему удовлетворению. Ура! - person user1160866; 20.05.2014
comment
Мне нравится использовать приведение (volatile void)expression, где expression само по себе является изменчивым, чтобы показать, что выражение явно оценивается с аппаратными побочными эффектами, таким образом, его не путают с (void)expression для отключения неиспользуемых переменных. Насколько я знаю, приведение volatile-выражений к (volatile void) не имеет дополнительного смысла, чем приведение к (void), но оно прекрасно самодокументируется. - person Thomas; 04.10.2016