Wskaźniki stałe i nietrwałe
Słowa kluczowe const i volatile zmieniają sposób traktowania wskaźników.Słowo kluczowe const określa, że wskaźnik nie może być modyfikowany po inicjacji; wskaźnik jest od tego czasu chroniony przed zmianami.
Słowo kluczowe volatile określa, że wartość skojarzona z nazwą, która po nim następuje, może być modyfikowana przez akcje inne niż te w aplikacji użytkownika.W związku z tym słowo kluczowe volatile jest przydatne do deklarowania obiektów w pamięci współdzielonej, do której może uzyskiwać dostęp wiele procesów lub globalnych obszarów danych dla komunikacji z procedurami usług przerwania.
Kiedy nazwa jest zadeklarowana jako volatile, kompilator ładuje ponownie wartość z pamięci przy każdym uzyskaniu dostępu do niej przez program.Znacznie zmniejsza to możliwości optymalizacji.Jednak gdy stan obiektu może się nieoczekiwanie zmieniać, jest to jedyny sposób zapewnienia przewidywalnej wydajności programu.
Aby zadeklarować obiekt wskazywany przez wskaźnik jako const lub volatile, użyj deklaracji w postaci:
const char *cpch;
volatile char *vpch;
Aby zadeklarować wartość wskaźnika - czyli rzeczywisty adres przechowywany we wskaźniku - jako const lub volatile, użyj deklaracji w postaci:
char * const pchc;
char * volatile pchv;
Język C++ zapobiega przypisaniom, które pozwalałyby na modyfikację obiektu lub wskaźnika zadeklarowanego jako const.Takie przypisania powodowałyby usunięcie informacji z którą obiekt lub wskaźnik został zadeklarowany, naruszając w ten sposób zamiar pierwotnej deklaracji.Rozważ następujące deklaracje:
const char cch = 'A';
char ch = 'B';
Posiadając wcześniej deklaracje dwóch obiektów (cch typu const char i ch typu char), następująca deklaracja/inicjacje są poprawne:
const char *pch1 = &cch;
const char *const pch4 = &cch;
const char *pch5 = &ch;
char *pch6 = &ch;
char *const pch7 = &ch;
const char *const pch8 = &ch;
Poniższa deklaracja/inicjacje są błędne.
char *pch2 = &cch; // Error
char *const pch3 = &cch; // Error
Deklaracja pch2 deklaruje wskaźnik, dzięki któremu można zmodyfikować stały obiekt i dlatego jest niedozwolona.Deklaracja pch3 określa, że pointer jest stałą, a nie obiektem; ta deklaracja jest niedozwolona z tego samego powodu, dla którego niedozwolona jest deklaracja pch2.
Poniższe osiem przypisań pokazuje przypisania poprzez wskaźnik i zmianę wartości wskaźnika dla wcześniejszych deklaracji; na chwilę obecną załóżmy, że inicjacja pch1 poprzez pch8 była poprawna.
*pch1 = 'A'; // Error: object declared const
pch1 = &ch; // OK: pointer not declared const
*pch2 = 'A'; // OK: normal pointer
pch2 = &ch; // OK: normal pointer
*pch3 = 'A'; // OK: object not declared const
pch3 = &ch; // Error: pointer declared const
*pch4 = 'A'; // Error: object declared const
pch4 = &ch; // Error: pointer declared const
Wskaźniki zadeklarowane jako volatile, albo jako połączenie const i volatile przestrzegają tych samych zasad.
Wskaźniki do obiektów const są często używane w deklaracjach funkcji w następujący sposób:
errno_t strcpy_s( char *strDestination, size_t numberOfElements, const char *strSource );
Poprzednia instrukcja deklaruje funkcję strcpy_s, gdzie dwa z trzech argumentów są wskaźnikiem typu do char.Ponieważ argumenty są przekazywane przez odwołanie, a nie przez wartość, funkcja będzie mogła swobodnie modyfikować zarówno strDestination jak i strSource, jeśli strSource nie zostało zadeklarowane jako const.Deklaracja strSource jako const zapewnia obiekt wywołujący o tym, że strSource nie może być zmienione przez wywoływaną funkcję.
[!UWAGA]
Ponieważ istnieje standardowa konwersja z typename * do const typename *, dozwolone jest przekazanie argumentu typu char * do strcpy_s.Jednak odwrotna sytuacja nie jest prawdziwa; nie istnieje niejawna konwersja usuwająca atrybut const z obiektu lub wskaźnika.
Wskaźnik const danego typu może być przypisany do wskaźnika tego samego typu.Jednakże wskaźnik, który nie jest const nie może być przypisany do wskaźnika const.W poniższym kodzie pokazano poprawne i niepoprawne przypisania:
// const_pointer.cpp
int *const cpObject = 0;
int *pObject;
int main() {
pObject = cpObject;
cpObject = pObject; // C3892
}
Poniższy przykład pokazuje sposób deklarowania obiektu jako const, jeśli posiadasz wskaźnik do wskaźnika do obiektu.
// const_pointer2.cpp
struct X {
X(int i) : m_i(i) { }
int m_i;
};
int main() {
// correct
const X cx(10);
const X * pcx = &cx;
const X ** ppcx = &pcx;
// also correct
X const cx2(20);
X const * pcx2 = &cx2;
X const ** ppcx2 = &pcx2;
}