Różnica między const i const volatile

89

Jeśli zadeklarujemy zmienną jak za volatilekażdym razem, gdy nowa wartość jest aktualizowana
Jeśli zadeklarujemy zmienną jako constto wartość tej zmiennej nie zostanie zmieniona

W takim razie const volatile int temp;
jaki jest pożytek z zadeklarowania zmiennej tempjak powyżej?
Co się stanie, jeśli zadeklarujemy jako const int temp?

user559208
źródło
Nie używałbyś const volatile int temp;w zakresie blokowym (tj. Wewnątrz { }), nie ma tam zastosowania.
MM

Odpowiedzi:

145

Obiekt oznaczony jako const volatilenie będzie mógł zostać zmieniony przez kod (zostanie zgłoszony błąd z powodu constkwalifikatora) - przynajmniej przez tę konkretną nazwę / wskaźnik.

volatileCzęść środków kwalifikacyjnych że kompilator nie może zoptymalizować lub przywrócenia porządku dostępu do obiektu.

W systemie wbudowanym jest to zwykle używane w celu uzyskania dostępu do rejestrów sprzętowych, które mogą być odczytywane i aktualizowane przez sprzęt, ale nie ma sensu zapisywać do nich (lub może to być błąd do zapisu).

Przykładem może być rejestr statusu portu szeregowego. Różne bity będą wskazywać, czy znak czeka na odczytanie, czy rejestr nadawczy jest gotowy do przyjęcia nowego znaku (tj. - jest pusty). Każdy odczyt tego rejestru stanu może skutkować inną wartością w zależności od tego, co jeszcze wydarzyło się w sprzęcie portu szeregowego.

Nie ma sensu zapisywanie do rejestru statusu (w zależności od konkretnej specyfikacji sprzętowej), ale musisz się upewnić, że każdy odczyt rejestru skutkuje rzeczywistym odczytem sprzętu - używając wartości z pamięci podręcznej z poprzedniego odczytu wygranego '' t informować o zmianach w stanie sprzętu.

Szybki przykład:

unsigned int const volatile *status_reg; // assume these are assigned to point to the 
unsigned char const volatile *recv_reg;  //   correct hardware addresses


#define UART_CHAR_READY 0x00000001

int get_next_char()
{
    while ((*status_reg & UART_CHAR_READY) == 0) {
        // do nothing but spin
    }

    return *recv_reg;
}

Jeśli te wskaźniki nie zostałyby oznaczone jako istoty volatile, może wystąpić kilka problemów:

  • test pętli while może odczytać rejestr stanu tylko raz, ponieważ kompilator mógłby założyć, że cokolwiek wskazywał, nigdy się nie zmieni (w teście pętli while ani w samej pętli nie ma nic, co mogłoby to zmienić). Jeśli wszedłeś do funkcji, gdy w sprzęcie UART nie czekał żaden znak, możesz skończyć w nieskończonej pętli, która nigdy się nie zatrzymuje, nawet gdy znak został odebrany.
  • odczyt rejestru odbiorczego mógłby zostać przeniesiony przez kompilator do przed pętlą while - ponownie, ponieważ w funkcji nie ma nic, co wskazuje na *recv_regzmianę przez pętlę, nie ma powodu, aby nie można go było odczytać przed wejściem do pętli.

W volatileKwalifikacje zapewnia, że te optymalizacje nie są wykonywane przez kompilator.

Michael Burr
źródło
5
+1 za wyjaśnienie. I mam pytanie: a co z metodami o stałej zmienności? Jeśli mam klasę, która jest dostępna przez wielu wątków (choć dostęp jest zsynchronizowany z mutex) nie moje metody const muszą być także lotne (ponieważ niektóre zmienne mogą być zmieniane przez inny wątek)
Sasa
39
  • volatile powie kompilatorowi, aby nie optymalizował kodu związanego ze zmienną, zwykle wtedy, gdy wiemy, że można go zmienić „z zewnątrz”, np. przez inny wątek.
  • const poinformuje kompilator, że programowi nie wolno modyfikować wartości zmiennej.
  • const volatileto bardzo wyjątkowa rzecz, którą prawdopodobnie zobaczysz używaną dokładnie 0 razy w swoim życiu (tm). Jak można się było spodziewać, oznacza to, że program nie może modyfikować wartości zmiennej, ale wartość może być modyfikowana z zewnątrz, przez co na zmiennej nie będą wykonywane żadne optymalizacje.
mingo
źródło
12
Pomyślałbym, że volatilezmienne są zwykle tym, co się dzieje, gdy zaczynasz bawić się sprzętem, a nie innymi wątkami. Tam, gdzie widziałem, const volatilejest używany w takich rzeczach, jak rejestry stanu mapowane w pamięci lub tym podobne.
TYLKO MOJA poprawna OPINIA
2
Oczywiście masz całkowitą rację, wielowątkowość to tylko jeden przykład, ale nie jedyny :).
mingos
25
Jeśli pracujesz z systemami wbudowanymi, zobaczysz to bardzo często.
Daniel Grillo
28

Nie dlatego, że zmienna jest stała, mogła nie ulec zmianie między dwoma punktami sekwencji.

Stałość to obietnica, którą składasz, aby nie zmieniać wartości, a nie, że wartość nie zostanie zmieniona.

Alexandre C.
źródło
9
Plus jeden za wskazanie, że constdane nie są „stałe”.
Bogdan Alexandru
7

Musiałem użyć tego we wbudowanej aplikacji, w której niektóre zmienne konfiguracyjne znajdują się w obszarze pamięci flash, który może być aktualizowany przez bootloader. Te zmienne konfiguracyjne są `` stałe '' w czasie wykonywania, ale bez kwalifikatora ulotnego kompilator zoptymalizowałby coś takiego ...

cantx.id = 0x10<<24 | CANID<<12 | 0;

... przez wstępne obliczenie stałej wartości i użycie natychmiastowej instrukcji montażu lub załadowanie stałej z pobliskiej lokalizacji, tak aby wszelkie aktualizacje oryginalnej wartości CANID w obszarze konfiguracji flash były ignorowane. CANID musi być stale niestabilny.

push2eject
źródło
7

W języku C, const i volatile są kwalifikatorami typu i te dwa są niezależne.

Zasadniczo const oznacza, że ​​wartość nie może być modyfikowana przez program.

A zmienna oznacza, że ​​wartość podlega nagłej zmianie (prawdopodobnie spoza programu).

W rzeczywistości standard C podaje przykład prawidłowej deklaracji, która jest zarówno stała, jak i zmienna. Przykładem jest

„Extern const volatile int real_time_clock;”

gdzie zegar czasu rzeczywistego może być modyfikowany sprzętowo, ale nie może być przypisany, zwiększany ani zmniejszany.

Dlatego powinniśmy już traktować oddzielnie const i volatile. Poza tym ten kwalifikator typu ma zastosowanie również do struktur, union, enum i typedef.

user2903536
źródło
5

Możesz używać const i volatile razem. Na przykład, jeśli zakłada się, że 0x30 jest wartością portu, który jest zmieniany tylko przez warunki zewnętrzne, następująca deklaracja zapobiegnie jakiejkolwiek możliwości wystąpienia przypadkowych skutków ubocznych:

const volatile char *port = (const volatile char *)0x30;
Krok
źródło
4

constoznacza, że ​​zmienna nie może być modyfikowana przez kod c, a nie że nie może się zmienić. Oznacza to, że żadna instrukcja nie może zapisać do zmiennej, ale jej wartość może się zmienić.

volatileoznacza, że ​​zmienna może się zmienić w dowolnym momencie i dlatego nie można używać żadnych wartości z pamięci podręcznej; każdy dostęp do zmiennej musi być wykonywany na adres jej pamięci.

Ponieważ pytanie jest oznaczone jako „osadzone” i zakładając, że tempjest to zmienna zadeklarowana przez użytkownika, a nie rejestr związany ze sprzętem (ponieważ są one zwykle obsługiwane w oddzielnym pliku .h), rozważ:

Wbudowany procesor, który ma zarówno ulotną pamięć danych do odczytu i zapisu (RAM), jak i nieulotną pamięć danych tylko do odczytu, na przykład pamięć FLASH w architekturze von-Neumanna, w której przestrzeń danych i programów współdzielą wspólną magistralę danych i adresów.

Jeśli zadeklarujesz, const tempże masz wartość (przynajmniej różną od 0), kompilator przypisze zmienną do adresu w przestrzeni FLASH, ponieważ nawet jeśli byłaby przypisana do adresu RAM, nadal potrzebuje pamięci FLASH do przechowywania wartości początkowej zmiennej, przez co adres RAM jest marnowaniem miejsca, ponieważ wszystkie operacje są tylko do odczytu.

W konsekwencji:

int temp;to zmienna przechowywana w pamięci RAM, inicjowana do 0 przy starcie (cstart), mogą być używane wartości z pamięci podręcznej.

const int temp;jest zmienną przechowywaną w (read-ony) FLASH, zainicjowaną na 0 w czasie kompilacji, mogą być używane wartości z pamięci podręcznej.

volatile int temp; jest zmienną przechowywaną w pamięci RAM, zainicjowaną do 0 podczas uruchamiania (cstart), wartości zapisane w pamięci podręcznej NIE będą używane.

const volatile int temp; jest zmienną przechowywaną w (read-ony) FLASH, zainicjowaną na 0 w czasie kompilacji, wartości z pamięci podręcznej NIE będą używane

Oto przydatna część:

Obecnie większość procesorów Embedded ma możliwość dokonywania zmian w swojej pamięci nieulotnej tylko do odczytu za pomocą specjalnego modułu funkcyjnego, który const int tempmożna zmienić w czasie wykonywania, choć nie bezpośrednio. Mówiąc inaczej, funkcja może modyfikować wartość pod adresem, pod którym tempjest przechowywana.

Praktycznym przykładem może być użycie tempnumeru seryjnego urządzenia. Przy pierwszym uruchomieniu wbudowany procesor tempbędzie równy 0 (lub zadeklarowana wartość) i funkcja może wykorzystać ten fakt do uruchomienia testu podczas produkcji i jeśli się powiedzie, poprosić o nadanie numeru seryjnego i zmodyfikować wartość tempza pomocą o specjalnej funkcji. Niektóre procesory mają specjalny zakres adresów z pamięcią OTP (jednorazowo programowalną) tylko do tego.

Ale oto różnica:

Jeśli const int tempjest to identyfikator, który można modyfikować zamiast jednorazowo programowalnego numeru seryjnego i NIE jest zadeklarowany volatile, wartość z pamięci podręcznej może być używana do następnego uruchomienia, co oznacza, że ​​nowy identyfikator może nie być ważny do następnego ponownego uruchomienia lub, co gorsza, niektóre funkcje może używać nowej wartości, podczas gdy inne mogą używać starszej wartości z pamięci podręcznej do ponownego uruchomienia. Jeśli const int tempjest zadeklarowane voltaile, zmiana ID zacznie obowiązywać natychmiast.

Michael Kusch
źródło
Wow, ta odpowiedź jest długa
2

Mówiąc prościej, wartość w zmiennej „const volatile” nie może być modyfikowana programowo, ale może być modyfikowana sprzętowo. Nietrwałe jest tutaj, aby zapobiec jakiejkolwiek optymalizacji kompilatora.

rajeshsam
źródło
1

Używamy słowa kluczowego „const” dla zmiennej, gdy nie chcemy, aby program ją zmieniał. Natomiast kiedy deklarujemy zmienną „const volatile”, mówimy programowi, aby jej nie zmieniał, oraz kompilatorowi, że zmienną tę można zmienić nieoczekiwanie na podstawie danych wejściowych pochodzących ze świata zewnętrznego.

Ali
źródło