const w języku C umożliwia ograniczenie modyfikowalności obiektu. Pracując z parametrami funkcji, pomaga to chronić dane przed przypadkowymi zmianami. Kluczowa różnica w deklaracji zależy od tego, do czego odnosi się modyfikator const i gdzie się znajduje względem wskaźnika.
Przykład różnych deklaracji:
void func(const int *ptr); // wskaźnik na stały int void func(int * const ptr); // stały wskaźnik na int void func(const int * const ptr); // stały wskaźnik na stały int
const int *ptr — dane są niezmienne, sam wskaźnik można przypisać na nowo.int *const ptr — dane są zmienne, ale wskaźnika nie można przypisać na nowo.const int *const ptr — ani dane, ani wskaźnik nie mogą być zmieniane wewnątrz funkcji.Poprawne użycie const: pozwala:
void print_array(const int *arr, size_t n) { for (size_t i = 0; i < n; ++i) { printf("%d\n", arr[i]); // arr[i] = 10; // błąd: próba zmiany danych const } }
Pytanie: Czy można przypisać adres zmiennej stałej do zwykłego wskaźnika?
Oczekiwany błędny odpowiedź: "Tak, jeśli usuniesz const w deklaracji wskaźnika, kompilator na to pozwala."
Poprawna odpowiedź: Dopuszczalne jest "obniżenie const" tylko z wyraźnym rzutowaniem typów (casting), ale prowadzi to do undefined behavior przy próbie zmiany obiektu zadeklarowanego jako const. Nie można tego robić — narusza to semantykę const i prowadzi do błędów w czasie wykonania.
Przykład:
const int x = 5; int *ptr = (int*)&x; *ptr = 10; // UB: zmiana obiektu const
Historia
W dużym projekcie programista próbował obejść ochronę const, rzutując wskaźnik const na zwykły i modyfikując dane w segmencie pamięci tylko do odczytu. Na niektórych platformach doprowadziło to do awaryjnego zakończenia programu (segmentation fault), a na innych — do niezauważalnych błędów, trudnych do debugowania.
Historia
W bibliotece do pracy z tablicą programista zapomniał zadeklarować parametry jako const. W rezultacie nieprawidłowe wywołanie funkcji przypadkowo zmieniło dane źródłowe, co doprowadziło do niesynchronizacji stanu tablicy i poważnych błędów w kolejnych blokach przetwarzania.
Historia
Podczas pisania funkcji callback przekazywanej do cudzej biblioteki, zapomniano specyfikować const dla bufora wejściowego. Biblioteka próbowała zmienić dane w stałej linii, co spowodowało awarię na niektórych systemach operacyjnych i długotrwałe rozważania na temat źródła problemu.