W niniejszym materiale, skonfrontujemy sobie 2 rodzaje tworzenia stałych w języku C 2️⃣. Stała w języku C może być dodana albo poprzez dyrektywę "#define", albo słowo kluczowe "const" 💡. Dlatego warto wiedzieć czym się różnią te podejścia między sobą, bo różnic jest o wiele więcej, niż możesz teraz przypuszczać 😳!
STAŁA W JĘZYKU C. PREZENTUJĘ DWA SPOSOBY NA JEJ UTWORZENIE!
Zacznę od strony historycznej, ponieważ ma to związek z tematem ⚠️.
Język C, gdybyś tego nie wiedział śmiertelniku 😁, stanowi "trzon" języków wysokiego poziomu jakie występują obecnie (pierwsze wzmianki sięgają 1969 roku). Język C nie był tworzony na taki wzór, jaki widzimy w wielu innych językach. Dotyczy to nie tylko stałych, lecz także wielu innych konstrukcji, zwłaszcza tak zwanych "lukrów składniowych" (ang. syntactic sugar), czyli składniowych "ułatwiaczy życia" 🍰.
Zarówno język C, jak i C++, są językami bardzo specyficznymi od strony składni, gdyż doczekały się pionierskich podejść ludzi celem zwiększenia czytelności kodu dla człowieka, porównując do asemblera. Ponadto zapewniają niskopoziomowy dostęp do wszystkich funkcji systemu, tak jak asembler (jednak będąc dalej językami wysokopoziomowymi) ℹ️.
Język C oraz C++, oprócz procesu kompilacji, przechodzi wcześniej jeszcze przez etap preprocesora. Jest to program, który wyszukuje wbudowanych dyrektyw (takich jak "#include" czy tytułowy "#define"), zamieniając je na odpowiednie treści, które ostatecznie lądują w kodzie wynikowym 🔥. "#define" dla przykładu, zostanie zamienione na stałą wartość, a "#include", "wklei" (dosłownie) zawartość dołączanego pliku nagłówkowego. Oto cała magia 🔮!
ZASTOSOWANIE STAŁEJ
Do tej pory nie zostało wyjaśnione o co chodzi z tą "stałą", więc wyjaśnijmy sobie i to 😄. "Stałą" nazywamy wartość w programie, która jest oznaczona jako niemodyfikowalna (tylko do odczytu). Raz zdefiniowanej wartości nie można zmienić, co automatycznie przynosi zrozumienie pojęcia "stałej" 😁.
Uważam, że stałe jak najbardziej powinny być stosowane w programach, z dwóch ważnych powodów 👇:
- nadajesz wartości kontekst,
- w przypadku konieczności modyfikacji, zmieniasz wartość tylko w jednym miejscu.
Mnie na przykład, dostrzeżenie w środek wyrażenia nazwy "NUMBER_OF_PAGES" powie dużo więcej, niż wstawiona "na chama" liczba 50 😅. Nawet jak mamy stałe znane choćby z matematyki np. słynna liczba PI, to i tak wszystkie "trzy czternastki" powinny mieć w programie etykietę "PI". To jest lepiej dla Ciebie i każdej innej osoby, która będzie operować na tym kodzie na co dzień 👍.
Drugi powód jest jeszcze ważniejszy ‼️. Gdyby Ci się zdarzyło kiedykolwiek zarządzać projektem mającym 1,000 plików źródłowych, to teraz wyobraź sobie tę męczarnię przedzierania się przez wszystkie pliki w poszukiwaniu stałych wartości i każdorazową modyfikację raz po raz 🥵! A tak, gdy jest zdefiniowana jedna stała w języku C i każde miejsce się do niej odwołuje, to wchodzisz do jednego pliku, zmieniasz i to wszystko ✅!
Definiując stałe w języku C musisz pamiętać o tym, że wybór dyrektywy "#define" albo słowa "const", ma znaczenie dla procesu "sklejania" programu, a co za tym idzie, obie metody mają swoje wady i zalety ⚠️!
PRZYKŁAD KODU ŹRÓDŁOWEGO
Na razie zerknijmy na 2 proste przykłady funkcji "main", które dają identyczny efekt: wypisują na strumieniu wyjściowym liczbę od 1 do N używając funkcji "printf" wewnątrz pętli "for" 📝.
UŻYCIE DYREKTYWY "#DEFINE"
Najpierw stała zdefiniowana przy użyciu dyrektywy "#define" 👇:
#include <stdio.h>
#define NUMBERS 30
int main(void)
{
for (int i = 1; i <= NUMBERS; ++i)
{
printf("Liczba %d\n", i);
}
return 0;
}Stała w języku C tworzona przez "#define", jest w zwyczaju umieszczana na samej górze, przed funkcją uruchomieniową i przed prototypami innych funkcji (jeśli istnieją). Zobacz, że postać jest dosyć nietypowa, jak na dzisiejsze standardy:
#define NUMBERS 30Zaraz po haśle "#define", podajemy nazwę. Standard programowania zaleca, aby nazwy stałych pisać wielkimi literami, a odstępy oznaczać znakiem podkreślenia (tzw. "podłogą"). To jest konwencja o nazwie "SCREAMING_SNAKE_CASE" i taką radzę stosować w każdym języku bez wyjątku ℹ️.
Potem wprowadzamy wartość jaką dana stała ma przyjmować i tu też jest ciekawie 💥. W obrębie dyrektywy poruszamy się na poziomie preprocesora, co oznacza, że na tym etapie nie ma jeszcze sprawdzania typów danych. Zatem, zostanie on wywnioskowany na podstawie treści ⚠️! Liczba 30, zatem będzie to "int" - liczba całkowita ✅.
Tak wygląda tworzenie stałej w języku C przy użyciu dyrektywy "#define".
Stała w języku C może być zdefiniowana poprzez zastosowanie dyrektywy "#define" - wtedy jest obsługiwana przez preprocesor.
UŻYCIE SŁOWA KLUCZOWEGO "CONST"
Teraz "const". Czy potrafisz dostrzec subtelną różnicę 🙂?
#include <stdio.h>
const int NUMBERS = 30;
int main(void)
{
for (int i = 1; i <= NUMBERS; ++i)
{
printf("Liczba %d\n", i);
}
return 0;
}Stała teraz przyjmuje taką postać:
const int NUMBERS = 30;Nie ma dyrektywy, za to na jej miejsce weszła definicja z użyciem "const". I co najważniejsze - zwróć uwagę, że tutaj już musimy określić typ danych 🤯! To dlatego, że "const" jest analizowane przez kompilator, który w przeciwieństwie do preprocesora, zwraca już uwagę na to, czy przestrzegamy zasad składni (a jedną z nich jest podanie typu danych 😝!)!
Stała w języku C może być także zdefiniowana poprzez zastosowanie słowa kluczowe "const" - wtedy jest przetwarzana przez kompilator.
DYREKTYWA "#DEFINE" CZY SŁOWO "CONST"?
Naturalnie to nie są wszystkie różnice jakie znajdują się przy podejściu z dyrektywą i przy podejściu ze słowem kluczowym ⚠️. Pełne zestawienie różnic znajdziesz w poniższej tabelce 👇:
| #define | const |
| to jest makrodefinicja | to jest słowo kluczowe |
| nie jest sprawdzany typ | jest sprawdzany typ |
| przetwarzanie przez preprocesor | przetwarzanie przez kompilator |
| można również definiować makra (funkcje z "#define") | można definiować tylko stałe wartości |
| wspiera tylko zasięg globalny | wspiera zasięg globalny i lokalny (w zależności od miejsca definicji) |
| można usunąć stałą przy pomocy "#undef" | nie można usunąć stałej (jeżeli jest lokalna, wtedy sama "wygasa" po wykonaniu całej funkcji) |
Króciutko rozwinę niektóre punkty 🔵.
"#DEFINE" DLA STAŁYCH...ORAZ FUNKCJI!
Warto podkreślić, że "#define" można stosować nie tylko do tworzenia stałych, lecz także do funkcji 🔥! Wtedy nazywamy to "makrem" albo "makrodefinicją". Możesz natrafić na dyrektywę zapisaną w takim stylu 👇:
#define ABS(N) ((N) < 0 ? -(N) : (N))To jest przykład makrodefinicji ✔️. I to możesz potem wstawiać w dowolnym miejscu w programie i używać jak normalnej funkcji 👍 - więcej informacji będzie w odrębnym artykule.
"CONST" WSPIERA ZASIĘG LOKALNY
W tym przypadku to "const" zdobywa punkty 🔢! Stała w języku C utworzona przez "const" może zostać wstawiona do środka funkcji będąc stałą lokalną (występującą wyłącznie w obrębie ciała funkcji) 👇:
#include <stdio.h>
int main(void)
{
const int NUMBERS = 30;
for (int i = 1; i <= NUMBERS; ++i)
{
printf("Liczba %d\n", i);
}
return 0;
}Może to nie robi dużego wrażenia, jednak Panie - różnica to różnica 😄!
"EDYCJA, COFNIJ"
Stała pochodząca od "#define" może zostać...wycofana 😱! Służy do tego dyrektywa "#undef" 👇:
#define NUMBERS 30
// instrukcje
#ifdef NUMBERS
#undef NUMBERS
#endifi wtedy możesz dosłownie "wycofać" zdefiniowaną stałą, sprawiając że w późniejszych instrukcjach nie będzie istniała 💥!
Tym razem wyszedł dłuższy artykuł mający na celu nie tylko zaprezentowanie dwóch postaci, ale także podkreślenie najważniejszych różnic pomiędzy tworzeniem stałych przy pomocy "#define", a "const" ✅.