Квалификатор volatile

Квалификатор volatile говорит компилятору, что оптимизировать данную переменную запрещено, и что компилятор никогда не сможет предвидеть или вычислить ее значение заранее.

volatile int flag = 42;

Когда это вообще нужно? Рассмотрим следующий пример:

bool cancel = false;
/* ... */

while (!cancel) {
    /* ... */
}

Если в теле цикла переменная cancel не меняется, то компилятор может соптимизировать ее проверку, и в цикл мы не войдем никогда.

Таким и должно быть наблюдаемое поведение, если мы не пишем сложной логики, например, если переменная cancel не шарится с другими потоками или процессами, которые могут ее перезаписывать. Использование volatile нужно для предупреждения компилятора, что магические силы могут изменить значение переменной в любой момент и нельзя опираться на то, что в нее было записано в compile-time.

Другой пример:

unsigned char* pControl = 0xff24;

void f() {
    *pControl = 1;
    *pControl = 0;
    *pControl = 0;
}

Компилятор может опустить первые две операции присваивания, оставив только последнюю. Когда это может быть вредно? При разработке программ, работающих с Memory mapped IO, то есть взаимодействующих с какими-то железками через оперативную память, или при разработке драйверов, где важно каждое переданное значение.

Использование volatile запрещает перегруппировку инструкций доступа и их оптимизацию, но ни в коем случае не гарантирует атомарности. Чтение volatile переменной при одновременной записи в нее из другого потока или одновременная запись из разных потоков без синхронизации - это data race.

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

Разумеется, нельзя использовать волатильную переменную со снятым позднее через const_cast квалификатором volatile - это undefined behavior.

Связь с квалификатором const

Как и const, volatile - это cv-квалификатор, разумеется, поэтому нужно строго понимать, куда писать это слово в объявлении типа.

Пример выше исправляется следующим образом, потому что в функции f мы делаем присвоения в unsigned char&:

volatile unsigned char* pControl = 0xff24;

Но если нужна именно volatile-переменная, а не данные по указателю, то это должно писаться следующим образом:

unsigned char* volatile pControl = 0xff24;

Заимствования:

volatile type qualifier - cppreference.com

Алёна C++: Ключевое слово volatile (alenacpp.blogspot.com)