Pytanie Czy wskaźnik może być niestabilny?


Rozważ następujący kod:

int square(volatile int *p)
{
    return *p * *p;
}

Teraz volatile słowo kluczowe wskazuje, że wartość w Lokalizacja pamięci może być zmieniana w sposób nieznany kompilatorowi inne nieznane skutki uboczne (np. modyfikacja przez przerwanie sygnału, rejestr sprzętowy lub pamięć I / O odwzorowane w pamięci), nawet jeśli nic nie ma w kod programu modyfikuje zawartość.

Co dokładnie dzieje się, gdy deklarujemy wskaźnik jako zmienny?

Czy powyższy kod zawsze działa, czy też różni się od tego:

int square(volatile int *p)
{
    int a = *p;
    int b = *p
    return a*b;
}

Czy możemy w końcu pomnożyć różne liczby, ponieważ wskaźniki są niestabilne?

Czy istnieje lepszy sposób na zrobienie tego?


21
2017-10-22 12:18


pochodzenie


To jest zmienny wskaźnik: int * volatile p, jest to wskaźnik wskazujący na zmienną int: volatile int *p - Zang MingJie
powiązane / dupe Dlaczego wskazówka point-volatile, taka jak "volatile int * p", jest przydatna? - NathanOliver
Ok zrozumiałem, jak to działa, ale moje pytanie brzmi, czy obie funkcje wymienione w Pytanie dadzą taki sam wynik? lub os tam jakikolwiek lepszy sposób użycia zadeklarować je - vishal
Gdyby p wskazuje na rejestr sprzętowy lub jakieś urządzenie zmapowane w pamięci, dlaczego chciałbyś pomnożyć dwa niezależne odczyty z tego? Jaki jest przypadek użycia? - Bo Persson
@vishal - kod willy będzie "działał", ponieważ otrzymujesz produkt. Jeśli jednak *p jest naprawdę podłączony do zewnętrznego sprzętu, może dostać różne wynik. Lub sprzęt może reagować inaczej na jeden lub dwa dostępy - jak wystrzelenie jednego lub dwóch pocisków. - Bo Persson


Odpowiedzi:


Czy wskaźnik może być volatile?

Absolutnie; dowolny typ, z wyłączeniem funkcji i odniesień, może być volatile-wykwalifikowany.

Zwróć uwagę, że wskaźnik niestabilności jest zadeklarowany T *volatile, nie volatile T*, który zamiast tego deklaruje a wskaźnik-do-lotny.

Lotny wskaźnik oznacza, że ​​wartość wskaźnika, to jest jego adres, a nie wartość wskazywana przez, może mieć skutki uboczne, które nie są widoczne dla kompilatora, gdy jest on dostępny; w związku z tym optymalizacje wynikające z "reguły" jeżeli "nie mogą być brane pod uwagę przy tych dostępach.


int square(volatile int *p) { return *p * *p; }

Kompilator nie może przyjąć tego odczytu *p pobiera tę samą wartość, więc buforowanie jej wartości w zmiennej jest niedozwolone. Jak sama mówisz, wynik może się różnić, a nie być kwadratem *p.

Konkretny przykład: załóżmy, że masz dwie tablice ints

int a1 [] = { 1, 2, 3, 4, 5 };
int a2 [] = { 5453, -231, -454123, 7565, -11111 };

i wskaźnik do jednego z nich

int * /*volatile*/ p = a1;

z pewną operacją na wskazanych elementach

for (int i = 0; i < sizeof(a1)/sizeof(a1[0]); ++i) 
       *(p + i) *= 2;

tutaj p należy przeczytać każdą iterację, jeśli ją wykonasz volatile ponieważ, być może, może rzeczywiście wskazywać a2 ze względu na zdarzenia zewnętrzne.


11
2017-10-22 12:59



Uwaga: cppreference ma wartość dobrą opis reguły as-if. - Shafik Yaghmour
Pytanie do wyjaśnienia: w twoim przykładzie (square), wartość wskaźnika (to znaczy adres pamięci, który p reprezentuje, a nie wartość int, która to *p da ci) nie może się zmienić, o ile rozumiem: p jest wartością lokalną przekazywaną do wartości przez funkcję square. W jakich warunkach mogła być wartość p (adres pamięci) ewentualnie się zmienia? Jeśli chcesz zmienić wskaźnik (aka uchwyt), to by było square(**p). Jaki jest sens "niestabilnego" wskaźnika? Wskaźnik na lotność ma dla mnie doskonały sens ... po prostu nie "zmienny wskaźnik". - Christopher Schultz
Mam problem ze zrozumieniem tego zdania: "Lotny wskaźnik oznacza, że ​​wskazana wartość (nie wartość wskazanej wartości) lub przechowywany adres, jeśli wolisz, może mieć efekty uboczne, które nie są widoczne dla kompilatora, gdy jest on dostępny;". Podejrzewam, że coś jest nie w porządku; może tylko literówka ("wskaźnik wartości" -> "pointere wartość "?), lub może coś bardziej fundamentalnego. W każdym razie możesz spróbować przeprogramować go, aby był mniej kłopotliwy. - Ilmari Karonen
@IlmariKaronen Dzięki za wskazanie; czy teraz ma to więcej sensu? - edmz
@ Christristschultz To zależy od konkretnego przypadku: p może być globalny lub lokalny, a jego adres nie może się zmienić podczas kopiowania. Należy jednak wziąć pod uwagę, że może on ulec zmianie ze zdarzeń, których kompilator naprawdę nie widzi, jak niektóre kody zespołu modyfikujące dany adres, w którym zapisany jest wskaźnik. Ale generalnie, volatile wskaźniki są rzadsze do znalezienia. - edmz


Tak, możesz oczywiście mieć lotny wskaźnik.

Lotne oznacza nie więcej i nie mniej niż to, że każdy dostęp do obiektu lotnego (dowolnego rodzaju) jest traktowany jako widoczny efekt uboczny, a zatem jest zwolniony z optymalizacji (w szczególności oznacza to, że dostęp nie może zostać zmieniony lub zwinięty lub zoptymalizowane w całości). Odnosi się to do czytania lub pisania wartości, do wywoływania funkcji członków i oczywiście do dereferencji.

Zwróć uwagę, że gdy poprzedni akapit mówi "zmienianie kolejności", a pojedynczy wątek wykonania zakłada się. Lotne nie zastąpi operacji atomowych ani muteksów / zamków.

W prostszych słowach, volatile ogólnie tłumaczy z grubsza "Nie optymalizuj, po prostu rób dokładnie tak, jak mówię".

W kontekście a wskaźnik, zapoznaj się z wzorcem użytkowania podanym przez znanego Chrisa Lattnera "Co każdy programista musi wiedzieć o nieokreślonym zachowaniu" artykuł (tak, ten artykuł dotyczy C, nie C ++, ale to samo dotyczy):

Jeśli korzystasz z kompilatora opartego na LLVM, możesz usunąć "zmienny" wskaźnik zerowy, aby uzyskać awarię, jeśli tego właśnie szukasz, ponieważ generatory i magazyny nie są zwykle dotykane przez optymalizator.


11
2017-10-22 12:34





Tak. int * volatile.

W C ++ słowa kluczowe zgodnie z typem / wskaźnikiem / odwołaniem idą po tokenie, jak int * const jest stałym wskaźnikiem do liczby całkowitej, int const * jest wskaźnikiem na stałą liczbę całkowitą, int const * const jest stałym wskaźnikiem do stałej liczby całkowitej e.t.c. Możesz napisać słowo kluczowe przed typem tylko, jeśli dotyczy pierwszego tokena: const int x jest równe int const x.


2
2017-10-22 12:35





The volatile słowo kluczowe jest wskazówką dla kompilatora (7.1.6.1/7):

Uwaga:   lotny   to wskazówka do implementacji, aby uniknąć agresywnej optymalizacji z udziałem obiektu   ponieważ wartość obiektu może zostać zmieniona za pomocą niewykrywalnej przez implementację. Ponadto,   dla niektórych wdrożeń,   lotny   może oznaczać, że do uzyskania dostępu potrzebne są specjalne instrukcje sprzętowe   obiekt. Widzieć    1.9    dla szczegółowej semantyki. Ogólnie rzecz biorąc, semantyka   lotny   są przeznaczone do być   to samo w C   ++   ponieważ są w C.   - nota końcowa   ]

Co to znaczy? Cóż, spójrz na ten kod:

bool condition = false;
while(!condition)
{
    ...
}

domyślnie kompilator będzie z łatwością optymalizował warunek (nie zmienia się, więc nie ma potrzeby sprawdzania go przy każdej iteracji). Jeśli jednak zadeklarujesz warunek jako volatile, optymalizacja nie zostanie wykonana.

Oczywiście możesz mieć zmienny wskaźnik i można napisać kod, który się z tego powodu zawiesi, ale fakt, że zmienna jest volative nie oznacza, że ​​koniecznie zostanie zmieniony z powodu jakiejś ingerencji z zewnątrz.


2
2017-10-22 12:36



Jesteś pewny? Kompilator może to łatwo zauważyć volatile bool condition = false nie jest odwzorowany w pamięci, nie jest rejestrem sprzętowym i znajduje się w zasięgu lokalnym, do którego moduł obsługi sygnału nie może uzyskać dostępu. Jak to zmienić? - Bo Persson
volatile nie jest "podpowiedź"; ma wyraźną semantykę: każdy dostęp może być częścią obserwowalnego zachowania. Można to uznać za podpowiedź, co jest później, mianowicie nieskuteczność optymalizacji ukierunkowanych na usunięcie dostępu. - edmz


Możesz skończyć się mnożeniem różnych liczb, ponieważ jest niestabilny i może zostać niespodziewanie zmieniony. Możesz spróbować czegoś takiego:

int square(volatile int *p)
{
int a = *p;
return a*a;
}

1
2017-10-22 12:35





Tak, wskaźnik może być zmienny, jeśli wskazywana przez niego zmienna może się nieoczekiwanie zmienić, nawet jeśli sposób, w jaki to się dzieje, nie wynika z kodu.

Przykładem jest obiekt, który może być modyfikowany przez coś, co jest zewnętrzne w stosunku do kontrolującego wątku, a kompilator nie powinien optymalizować.

Najbardziej prawdopodobnym miejscem użycia zmiennego specyfikatora jest kod niskiego poziomu, który zajmuje się bezpośrednio sprzętem i gdzie mogą wystąpić nieoczekiwane zmiany.


1
2017-10-22 12:50



Nie. Lotny wskaźnik nie jest tym samym, co wskaźnik do zmiennej lotnej. - markgz