Menu

Stałe w C: const, #define i enum

Trzy sposoby na nazwanie wartości, która w C nigdy się nie zmienia: słowo kluczowe const, makra #define i stałe enum. Czym każdy z nich naprawdę jest i po który sięgać w danej sytuacji.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Stała to wartość z nazwą i obietnicą, że się nie zmieni. C daje ci do tego trzy mechanizmy, a pod spodem to naprawdę różne rzeczy: jeden jest zmienną, drugi podstawieniem tekstu, a trzeci typem całkowitym.

Po co w ogóle nazywać wartość

Porównaj te dwie linie:

double area = 3.14159 * r * r;
double area = PI * r * r;

Druga jest lepsza z trzech powodów, które wszystkie wychodzą na jaw później. Mówi, co znaczy liczba. Pojawia się w dokładnie jednym miejscu, więc zmiana dokładności to jedna edycja, a nie wyszukiwanie. A literówka w PI to błąd kompilacji, podczas gdy literówka w 3.14195 to błędny wynik.

Nienazwane liczby rozsiane po kodzie nazywa się magicznymi liczbami, a ich usunięcie to jedno z najtańszych ulepszeń, jakie możesz wprowadzić w programie w C.

const

Słowo kluczowe const oznacza zmienną jako tylko do odczytu:

Odkomentuj przypisanie, a kompilator odmówi: assignment of read-only variable 'PI'. Ten błąd w czasie budowania to cały sens.

Zmienna const to prawdziwa zmienna. Ma typ, więc kompilator sprawdza, jak jej używasz. Respektuje zasięg, więc const wewnątrz funkcji nie wycieka na zewnątrz. Ma adres, więc możesz wziąć do niej wskaźnik. A debugger pokaże ci jej nazwę i wartość.

const musi zostać zainicjalizowane w miejscu deklaracji, bo drugiej szansy nie ma:

const int limit;       /* błąd (albo śmieci na zawsze): nic nie może później nadać mu wartości */
const int limit = 50;  /* poprawnie */

#define

Alternatywą z preprocesora jest makro:

To nie jest zmienna. Zanim uruchomi się kompilator, preprocesor zastępuje każde wystąpienie PI w kodzie źródłowym znakami 3.14159265358979. Kompilator w ogóle nie widzi nazwy i właśnie dlatego #define nie ma typu i nie może pojawić się w debuggerze.

Zwróć uwagę, że nie ma średnika ani =. Makro to surowy tekst, więc ten częsty błąd:

#define MAX_USERS 100;        /* zwróć uwagę na średnik */

int limit = MAX_USERS + 1;    /* rozwija się do: int limit = 100; + 1;  -> błąd */

daje błąd w linii, która w niczym nie przypomina definicji. Konwencja pisania makr wielkimi literami istnieje właśnie po to, żeby czytelnik wiedział, że zachodzi podstawienie.

Makra zwyczajowo zapisuje się jako UPPER_CASE; zmienne const zwykle stosują tę samą konwencję, gdy reprezentują tę samą ideę.

Stałe enum

Trzecia forma daje zestaw nazwanych liczb całkowitych:

Wartości zaczynają się od 0 i rosną o jeden, chyba że je przypiszesz, więc MON to 0, a FRI to 4. Anonimowe enum { ... } to częsty idiom dla zwykłych stałych całkowitych.

Stałe enum mają prawdziwą przewagę nad obiema alternatywami: są stałymi całkowitymi czasu kompilacji, które kompilator rozumie, i są widoczne w debuggerze. Pełną historię znajdziesz w artykule o enumach w C.

Której użyć

PotrzebaUżyj
Stała z typem, zasięgiem i widoczna w debuggerzeconst
Rozmiar tablicy, etykieta case, szerokość pola bitowegoenum albo #define
Stała tekstowa#define albo const char *
Stała zmiennoprzecinkowaconst double
Zestaw powiązanych wartości całkowitychenum
Wszystko, co musi widzieć preprocesor (#if, strażniki dołączania)#define

Domyślnym wyborem powinno być const. Sięgaj po #define, gdy wartość musi istnieć przed kompilacją, a po enum, gdy potrzebujesz liczby całkowitej czasu kompilacji albo nazwanej grupy.

Jedyne miejsce, w którym to naprawdę gryzie, to rozmiary tablic:

const int w C to zmienna tylko do odczytu, a nie stałe wyrażenie czasu kompilacji. Wewnątrz funkcji C99 akceptuje int local[const_size], robiąc z tego tablicę o zmiennej długości; na poziomie pliku, w struct albo jako etykieta case zostaje to od razu odrzucone. enum i #define działają wszędzie.

(To jedna z prawdziwych różnic w stosunku do C++, gdzie const int jest stałym wyrażeniem. Kod przenoszony między tymi językami się na tym potyka.)

const ze wskaźnikami

const i wskaźniki łączą się na trzy sposoby, a ich znaczenia nie są wymienne. Czytaj deklarację od nazwy na zewnątrz:

const char *p;          /* p wskazuje na znaki, których nie wolno modyfikować */
char * const q;         /* samego q nie można przestawić; znaki mogą się zmieniać */
const char * const r;   /* nie może się zmienić ani wskaźnik, ani znaki */

Formę const char * będziesz pisać najczęściej, bo to właściwy typ dla parametru funkcji, który czyta string, nie zmieniając go:

Dodanie const do parametru tylko do odczytu nic nie kosztuje, a dokumentuje umowę w sposób, który egzekwuje kompilator. Warto to robić wszędzie.

Literały stringowe są w praktyce const

Subtelna pułapka, do której wyłapywania istnieje const:

char *s = "hello";   /* poprawne w C, ale s wskazuje na pamięć tylko do odczytu */
s[0] = 'H';          /* niezdefiniowane zachowanie: zwykle awaria */

Literały stringowe leżą w sekcji pliku wykonywalnego przeznaczonej tylko do odczytu. Zapis przez ten wskaźnik zwykle kończy się segfaultem. Zadeklarowanie wskaźnika jako const char *s = "hello"; sprawia, że kompilator odrzuci przypisanie, zamiast pozwolić programowi wysypać się w czasie działania.

Jeśli potrzebujesz modyfikowalnego stringa, skopiuj go do tablicy:

Najczęściej zadawane pytania

Jak zadeklarować stałą w C?

Dodaj const do deklaracji i nadaj wartość: const double PI = 3.14159;. Kompilator odrzuci wtedy każdą próbę przypisania. Starsza alternatywa to makro preprocesora, #define PI 3.14159, które zamiast tego wykonuje podstawienie tekstu.

Jaka jest różnica między const a #define w C?

const tworzy prawdziwą zmienną z typem, o której kompilator wie, więc sprawdza typy, pojawia się w debuggerze i respektuje zasięg. #define to czyste podstawienie tekstu wykonywane przed kompilacją: nie ma typu ani zasięgu i nie da się go podejrzeć podczas debugowania. Wybieraj const, chyba że potrzebujesz wartości w miejscu, które wymaga stałej czasu kompilacji.

Czy zmiennej const można użyć jako rozmiaru tablicy w C?

Nie dla tablicy o stałym rozmiarze na poziomie pliku. const int N = 10; int arr[N]; to w C99 tablica o zmiennej długości (dozwolona wewnątrz funkcji, ale nie na poziomie pliku i nie w strukturze). Dla prawdziwego rozmiaru znanego w czasie kompilacji użyj #define N 10 albo enum { N = 10 };.

Co oznacza const char *p?

To wskaźnik na stały znak: możesz zmienić, na co wskazuje p, ale nie znak, na który wskazuje. char * const p to odwrotność, czyli stały wskaźnik na modyfikowalny znak. const char * const p blokuje jedno i drugie. Czytaj deklarację od nazwy na zewnątrz.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ