6.10 Когда происходит обращение к volatile-объекту?
В языке C существует понятие volatile-объектов. Обычно к ним обращаются через указатели и используют для доступа к аппаратному обеспечению или для взаимодействия между потоками. Стандарт рекомендует компиляторам воздерживаться от оптимизаций, связанных с обращением к volatile-объектам, но оставляет на усмотрение реализации определение того, что считается обращением к volatile-объекту. Минимальное требование заключается в том, что в точке следования все предшествующие обращения к volatile-объектам завершены, а последующие обращения ещё не происходили. Таким образом, реализация вправе переупорядочивать и объединять обращения к volatile-объектам, происходящие между точками следования, но не может делать этого для обращений по разные стороны от точки следования. Использование volatile не позволяет нарушать ограничение, запрещающее многократно изменять объекты между двумя точками следования.
Обращения к non-volatile-объектам не упорядочены относительно обращений к volatile-объектам. Нельзя использовать volatile-объект как барьер памяти для упорядочивания последовательности записей в non-volatile-память. Например:
int *ptr = something; volatile int vobj; *ptr = something; vobj = 1;
Если *ptr и vobj не могут ссылаться на одну и ту же область памяти, нет гарантии, что запись в *ptr произойдёт к моменту обновления vobj. Если вам нужна такая гарантия, необходимо использовать более строгий барьер памяти, например:
int *ptr = something;
volatile int vobj;
*ptr = something;
asm volatile ("" : : : "memory");
vobj = 1;
Чтение скалярного volatile-объекта происходит при обращении к нему в контексте void:
volatile int *src = somevalue; *src;
Такие выражения являются rvalue, и GCC реализует их как чтение volatile-объекта, на который указывает указатель.
Операции присваивания также являются выражениями и имеют rvalue. Однако при присваивании скалярному volatile-объекту сам volatile-объект не перечитывается независимо от того, используется ли rvalue выражения присваивания. Если rvalue присваивания используется, его значение совпадает со значением, присвоенным volatile-объекту. Например, ни в одном из следующих случаев vobj не читается:
int obj; volatile int vobj; vobj = something; obj = vobj = something; obj ? vobj = onething : vobj = anotherthing; obj = (something, vobj = anotherthing);
Если после выполнения присваивания вам необходимо прочитать volatile-объект, используйте отдельное выражение с промежуточной точкой следования.
Поскольку к битовым полям нельзя обращаться по отдельности, при записи в volatile-битовые поля или обращении к соседним битовым полям может происходить их неявное чтение. Операции с битовыми полями могут быть оптимизированы так, что соседние битовые поля будут считываться лишь частично, если они пересекают границу единицы хранения. По этим причинам не рекомендуется использовать volatile-битовые поля для доступа к аппаратному обеспечению.
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-15.3.0/gcc/Volatiles.html