Wiele programów niskiego poziomu używa zmiennego słowa kluczowego dla typów do mapowania pamięci i tak dalej, jednak jestem trochę zdezorientowany co do tego, co NAPRAWDĘ robi w tle. Innymi słowy, co to znaczy, że kompilator nie „optymalizuje” adresu pamięci?
9
volatile
zmiennej, która mówi 5, i czytasz ją ponownie w przyszłym roku, masz gwarancję, że dostaniesz 6.Odpowiedzi:
volatile
oznacza, że jakiś inny procesor lub urządzenie I / O lub coś może zmienić zmienną spod ciebie.W przypadku zwykłej zmiennej kroki twojego programu są jedyną rzeczą, która ją zmieni. Na przykład, jeśli czytasz
5
zmienną i jej nie zmieniasz, nadal będzie zawierała5
. Ponieważ możesz na tym polegać, twój program nie musi poświęcić czasu na ponowne odczytanie zmiennej następnym razem, gdy będziesz chciał jej użyć. Kompilator C ++ jest inteligentny do generowania kodu, który po prostu pamięta5
.Ale możesz to odczytać jako
5
, być może wtedy system ładuje dane z dysku do tej pamięci, zmieniając je na500
. Jeśli chcesz, aby Twój program odczytał nową wartość500
, potrzebujesz, aby kompilator nie był zbyt inteligentny w używaniu poprzednio odczytanego5
. Musisz powiedzieć mu, aby za każdym razem ładował wartość. Właśnie tovolatile
robi.Analogia dla 5-latków
Załóżmy, że stawiasz duży arkusz papieru na stole. W jednym rogu papieru, należy zanotować aktualny wynik trwającej grze
3 to 4
. Następnie idziesz na drugą stronę stołu i zaczynasz pisać historię o grze. Twój przyjaciel, który ogląda grę, aktualizuje wynik w tym rogu w trakcie gry. Kasuje3 to 4
i pisze3 to 5
.Gdy umieścisz wynik gry w swojej historii, możesz:
3 to 4
wesoło zakładając, że się nie zmienił (lub nie przejmując się, jeśli tak się stało), lub3 to 5
teraz) i wróć. Takvolatile
działa zmienna.źródło
volatile
oznacza dwie rzeczy:Wartość zmiennej może ulec zmianie bez zmiany twojego kodu. Dlatego, gdy kompilator odczytuje wartość zmiennej, to może nie przyjąć, że jest taka sama jak ostatni raz było czytać, lub że jest taki sam, jak ostatniej wartości przechowywanej, ale należy jeszcze raz przeczytać.
Zapisywanie wartości zmiennej lotnej jest „efektem ubocznym”, który można zaobserwować z zewnątrz, więc kompilatorowi nie wolno usuwać zapisu wartości; na przykład jeśli dwie wartości są przechowywane w jednym rzędzie, to kompilator musi faktycznie zapisać tę wartość dwukrotnie.
Jako przykład:
Kompilator musi zapisać numer dwa, odczytać zmienną i, zapisać zmienną, którą odczytał do i.
Jest jeszcze inna sytuacja: jeśli funkcja używa,
setjmp
a następnielongjmp
jest wywoływana, gwarantuje się, że wszystkie zmienne lokalne zmienne funkcji mają ostatnią zapamiętaną wartość - nie jest tak w przypadku nieulotnych zmiennych lokalnych.źródło
i
i wartośćpi = &i
, wówczasx = *pi
dokonuje odczytu zi
, ale nie gwarantuje się, że ten odczyt będzie miał zmienną semantykę.i
zadeklarowano jako,volatile int i
topi
należy zadeklarować jakovolatile int *pi
, w którym*pi
to przypadku dostęp jest niestabilny, nie?Wyjaśnienie abstrakcyjne
Zarówno C, jak i C ++ mają koncepcję abstrakcyjnej maszyny . Gdy kod używa wartości jakiejś zmiennej, maszyna abstrakcyjna mówi, że implementacja musi uzyskać dostęp do wartości tej zmiennej. Kod formularza
statement_A; statement_B; statement_C;
należy wykonać w dokładnie określonej kolejności. Wyrażenia wspólne dla tych trzech instrukcji muszą być ponownie obliczane za każdym razem, gdy występują.W maszynach abstrakcyjnych, biorąc pod uwagę sekwencję instrukcji
statement_A; statement_B; statement_C;
, implementacja musi najpierw działaćstatement_A
w całości, a następnie wstatement_B
końcustatement_C
. Implementacja nie pamięta, że przypisałeśage
wartość 5. Każda instrukcja, do której się odwołuje,age
musi zamiast tego uzyskać dostęp do wartości tej zmiennej.Słowo kluczowe nie byłoby potrzebne,
volatile
jeśli implementacje ściśle wykonałyby kod C lub C ++ zgodnie ze specyfikacjami abstrakcyjnej maszyny. Maszyny abstrakcyjne C i C ++ nie mają pojęcia rejestrów, nie są powszechnie stosowane podwyrażenia, a kolejność wykonywania jest ścisła.Oba języki mają również takie same zasady. Implementacja jest zgodna ze standardem, pod warunkiem, że implementacja zachowuje się tak, jakby wykonała wszystko zgodnie ze specyfikacją abstrakcyjnej maszyny. Kompilator może zakładać, że zmienne nielotne nie zmieniają wartości między przypisaniami. Dopóki nie złamie
as-if
reguły, sekwencjastatement_A; statement_B; statement_C;
może zostać zaimplementowana przez wykonanie częścistatement_C
, następnie częścistatement_A
, następnie wszystkichstatement_B
, a następnie resztystatement_A
, a na końcu resztystatement_C
.Te zasady „ jak gdyby” nie mają zastosowania do
volatile
zmiennych. W odniesieniu dovolatile
zmiennych i funkcji, implementacja musi robić dokładnie to, co jej nakazałeś, i dokładnie w takiej kolejności, w jakiej kazałeś to robić.Ta abstrakcyjna specyfikacja maszyny ma wadę: jest powolna. Pozytywnym aspektem C i C ++ w porównaniu do innych języków jest to, że są one dość szybkie. Nie byłoby tak, gdyby kod został wykonany dla tych abstrakcyjnych maszyn. Na jak-jeśli zasady są co umożliwi C i C ++ się tak szybko.
Odpowiedź ELI5
„Optymalizacja” adresu pamięci to zaawansowana koncepcja, coś, co nie mieści się w zakresie możliwości pięciolatka. Pięciolatki zgodne z przepisami zrobią dokładnie to, co im każesz, nie więcej, nie mniej. Dzięki
volatile
, mówisz realizację zachowywać się jak to jest pięć: Nie myślenia, nie ma optymalizacje fantazyjne. Zamiast tego implementacja musi robić dokładnie to, co nakazuje kod.źródło
(nie) zmienna jest wskazówką dla kompilatora, w jaki sposób zoptymalizować kod (z punktu widzenia generowanego kodu asemblera):
źródło
Odpowiedzi wydają się dość spójne, ale brakuje ważnego punktu. Mówisz kompilatorowi, że chcesz przydzielić miejsce i dla każdego dostępu, czytaj LUB NAPISZ, chcesz, aby ten dostęp był wykonywany. Z jakiegoś powodu nie chcemy, aby optymalizowała dostęp lub zmienną.
Tak, jednym z powodów jest to, że ktoś inny może dla nas zmienić tę wartość. Innym powodem może być zmiana tej wartości dla kogoś innego. Ktoś inny, to ten, który go zmienia dla nas lub ten, dla którego go zmieniamy, może być sprzętem / logiką lub oprogramowaniem. Jest często używany do definiowania dostępu do rejestrów kontrolnych i statusowych w programach osadzonych na gołym metalu, zapisywania lub odczytu ze sprzętu. Jak również oprogramowanie rozmawiające z oprogramowaniem wyjaśnione w innych odpowiedziach.
Zobaczysz również zmienność używaną do kontrolowania, kiedy i w jakiej kolejności mają się pojawiać dostępy, jeśli próbujesz zmierzyć sekcję kodu, i nie używasz zmienności, o których mowa (czas rozpoczęcia, czas zakończenia i różnica) muszą być tylko obliczony pod koniec kompilator może swobodnie przesuwać dowolny pomiar czasu (nie w miejscu, w którym go umieściliśmy), nie dlatego, że nie może się zmieniać przy zmienności, ale doświadczenie pokazuje, że jest mniej prawdopodobne.
Czasami zobaczysz, że po prostu pali czas, elementarny migacz ledowy, cześć świata z gołego metalu, może użyć lotnego dla zmiennej, która liczy się do dużej liczby, tylko po to, aby ludzkie oko zobaczyło led zmień stan. Bardziej zaawansowane przykłady niż użycie timerów lub innych zdarzeń do spalenia czasu.
źródło