Ja dodam jeszcze że volatile wykorzystuje się przede wszystkim w obiektach, które mogą się spontanicznie zmienić bez wiedzy kompilatora.
Rozważmy kod, który odbiera 3 bajtu z kontrolera uart
Kopiuj
{
uint8_t buf[3] = {0};
uint8_t *dr = (uint8_t *)0x40001005;
buf[0] = *dr;
buf[1] = *dr;
buf[2] = *dr;
}
Po odczycie rejestru dr, kontroler zapisuje do rejestru dr następny bajt w kolejce (pomijam tutaj ustawienia flag). Kompilator tutaj może stwierdzić, że skoro w całym scopie, dr się nie zmienia, to on sobie tylko raz odczyta to co jest pod dr, a następnie będzie tylko brał wartość z rejestru. I dostajemy 3 takie same bajty - błędne.
Aby temu zaradzić do dr dodajemy słówko volatile. W takim przypadku, kompilator wie, że dr może się zmienić spontanicznie, bez udziału programu i każde odwołanie do komórki wskazującej przez dr, będzie zawsze pobierane bezpośrednio spod tego adresu.