Download as pdf or txt
Download as pdf or txt
You are on page 1of 20

Rozdział 3.

Zmienne i stałe
Program musi mieć możliwość przechowywania danych, z których korzysta. Dzięki zmiennym i
stałym mamy możliwość reprezentowania, przechowywania i manipulowania tymi danymi.
Z tego rozdziału dowiesz się
• jak deklarować i definiować zmienne oraz stałe,
• jak przypisywać wartości zmiennym oraz jak nimi manipulować,
• jak wypisywać wartość zmiennej na ekranie.

Czym jest zmienna?


W C++ zmienna służy do przechowywania informacji – jest to miejsce w pamięci komputera, w
którym możesz umieścić wartość, i z którego możesz ją później odczytać.
Zwróć uwagę, że jest to tymczasowe miejsce przechowywania. Gdy wyłączysz komputer,
wszystkie zmienne zostają utracone. Przechowywanie trwałe przebiega zupełnie inaczej. Zwykle
zmienne są przechowywane trwale dzięki umieszczeniu ich w bazie danych lub w pliku na dysku.
Przechowywanie w pliku na dysku zostanie omówione w rozdziale 16., „Zaawansowane
dziedziczenie.”

Dane są przechowywane w pamięci


Pamięć komputera można traktować jako szereg pojemników, ułożonych jeden za drugim. Każdy
pojemnik — czyli miejsce w pamięci — jest oznaczony kolejnym numerem. Te numery są
nazywane adresami pamięci. Zmienna rezerwuje jeden lub więcej pojemników, w których może
przechowywać wartość.
Nazwa zmiennej (na przykład myVariable) stanowi etykietkę jednego z tych pojemników; dzięki
niej można go łatwo zlokalizować nie znając rzeczywistego adresu pamięci. Rysunek 3.1
przedstawia schemat przebiegu tego procesu. Jak widać, zmienna myVariable rozpoczyna się od
adresu 103. W zależności od rozmiaru tej zmiennej, może ona zająć w pamięci jeden lub więcej
adresów.

Rysunek 3.1. Schematyczna reprezentacja pamięci

UWAGA Skrót RAM oznacza Random Access Memory (pamięć o dostępie swobodnym). Gdy
uruchamiasz program, jest on ładowany z pliku na dysku do pamięci RAM. W pamięci RAM
tworzone są wszystkie zmienne. Gdy programiści używają terminu „pamięć”, zwykle mają na
myśli pamięć RAM, do której się odwołują.

Przydzielanie pamięci
Gdy definiujesz zmienną w C++, musisz poinformować kompilator o jej rodzaju: czy jest to liczba
całkowita, znak, czy coś innego. Ta informacja mówi kompilatorowi, ile miejsca ma
zarezerwować dla zmiennej oraz jaki rodzaj wartości będzie w niej przechowywany.
Każdy pojemnik ma rozmiar jednego bajtu. Jeśli tworzona zmienna ma rozmiar czterech bajtów, to
wymaga czterech bajtów pamięci, czyli czterech pojemników. Typ zmiennej (na przykład liczba
całkowita) mówi kompilatorowi, ile pamięci (pojemników) ma przygotować dla zmiennej.
Swojego czasu programiści musieli znać się na bitach i bajtach, gdyż stanowią one podstawowe
jednostki przechowywania wszelkiego rodzaju danych. Programy komputerowe pozwalają na
ucieczkę od tych szczegółów, ale jest w dalszym ciągu pomocna wiedza o przechowywaniu
danych. Krótki przegląd podstaw matematyki dwójkowej możesz znaleźć w dodatku A, „Binarnie
i szesnastkowo.”

UWAGA Jeśli przeraża cię matematyka, nie przejmuj się dodatkiem A; tak naprawdę nie jest ci
potrzebny. Programiści nie muszą już być równocześnie matematykami, choć umiejętność
logicznego i racjonalnego myślenia jest zawsze pożądana.
Rozmiar liczb całkowitych
W danym komputerze każdy typ zmiennych zajmuje stałą ilość miejsca. Oznacza to, że liczba
całkowita może mieć w jednym komputerze dwa bajty, w innym cztery, lecz w danym komputerze
ma zawsze ten sam, niezmienny rozmiar.
Zmienna typu char (używana do przechowywania znaków) ma najczęściej rozmiar jednego bajtu.
Krótka liczba całkowita (short) ma w większości komputerów rozmiar dwóch bajtów, zaś długa
liczba całkowita (long) ma zwykle cztery bajty. Natomiast liczba całkowita (bez słowa
kluczowego short lub long) może mieć dwa lub cztery bajty. Można przypuszczać, że język
powinien to określać precyzyjnie, ale tak nie jest. Ustalono jedynie, że typ short musi mieć
rozmiar mniejszy lub równy typowi int (integer, liczba całkowita), który z kolei musi mieć
rozmiar mniejszy lub równy typowi long.
Najprawdopodobniej jednak pracujesz z komputerem, w którym typ short ma dwa bajty, zaś typy
int i long mają po cztery bajty.
Rozmiar liczb całkowitych jest wyznaczany przez procesor (16 lub 32 bity) oraz kompilator. W
nowoczesnych, 32-bitowych procesorach Pentium z najnowszymi kompilatorami (na przykład
Visual C++4 lub nowsze), liczby całkowite mają cztery bajty. W tej książce zakładamy, że liczby
całkowite (typ int) mają cztery bajty, choć w twoim przypadku może być inaczej.
Znak jest pojedynczą literą, cyfrą lub symbolem i zajmuje pojedynczy bajt pamięci.
Skompiluj i uruchom w swoim komputerze listing 3.1; pokaże on dokładny rozmiar każdego z
tych typów.
Listing 3.1. Sprawdzanie rozmiarów typów zmiennych istniejących w twoim komputerze
0: #include <iostream>
1:
2: int main()
3: {
4: using std::cout;
5:
6: cout << "Rozmiar zmiennej typu int to:\t\t"
7: << sizeof(int) << " bajty.\n";
8: cout << "Rozmiar zmiennej typu short int to:\t"
9: << sizeof(short) << " bajty.\n";
10: cout << "Rozmiar zmiennej typu long int to:\t"
11: << sizeof(long) << " bajty.\n";
12: cout << "Rozmiar zmiennej typu char to:\t\t"
13: << sizeof(char) << " bajty.\n";
14: cout << "Rozmiar zmiennej typu float to:\t\t"
15: << sizeof(float) << " bajty.\n";
16: cout << "Rozmiar zmiennej typu double to:\t"
17: << sizeof(double) << " bajty.\n";
18: cout << "Rozmiar zmiennej typu bool to:\t"
19: << sizeof(bool) << " bajty.\n";
20:
21: return 0;
22: }

Wynik
Rozmiar zmiennej typu int to: 4 bajty.
Rozmiar zmiennej typu short int to: 2 bajty.
Rozmiar zmiennej typu long int to: 4 bajty.
Rozmiar zmiennej typu char to: 1 bajty.
Rozmiar zmiennej typu float to: 4 bajty.
Rozmiar zmiennej typu double to: 8 bajty.
Rozmiar zmiennej typu bool to: 1 bajty.

UWAGA W twoim komputerze rozmiary zmiennych mogą być inne.

Większość listingu 3.1 powinna być ci znana. Podzieliłem linie tak, aby mieściły się na całej
stronie książki. W rzeczywistości linie 6. i 7. powinny stanowić linię pojedynczą. Kompilator
ignoruje tak zwane białe spacje (spacje, tabulatory, przejścia do nowej linii), więc traktuje linie 6. i
7. jak jedną całość.
Nowym elementem w tym programie jest użycie w liniach od 6. do 19. operatora (funkcji)
sizeof(). Ten operator jest dostarczany przez kompilator; informuje on o rozmiarze obiektu
przekazywanego mu jako parametr. Na przykład w linii 7., do operatora sizeof() jest
przekazywane słowo kluczowe int. Za pomocą tego operatora byłem w stanie sprawdzić że w
moim komputerze zmienne typu int mają ten sam rozmiar, co zmienne typu long, czyli cztery
bajty.

Zapis ze znakiem i bez znaku


Wszystkie typy całkowite występują w dwóch odmianach: signed (ze znakiem) oraz unsigned
(bez znaku). Czasem potrzebna jest liczba ujemna, a czasem dodatnia. Liczby całkowite (krótkie i
długie) bez słowa kluczowego unsigned są traktowane jako liczby ze znakiem. Liczby całkowite
signed są albo dodatnie albo ujemne, zaś liczby całkowite unsigned są zawsze dodatnie.
Liczby ze znakiem i liczby bez znaku mają po tyle samo bajtów, więc największa liczba, jaką
można przechować w zmiennej całkowitej bez znaku jest dwa razy większa niż największa liczba
dodatnia jaką można przechować w zmiennej całkowitej ze znakiem. Zmienna typu unsigned
short może pomieścić wartości od 0 do 65 535. Połowa tych wartości (reprezentowana przez
zmienną typu signed short) jest ujemna, więc zmienna tego typu może przechowywać jedynie
wartości od –32 768 do 32 767. Jeśli wydaje ci się to skomplikowane, zajrzyj do dodatku A.

Podstawowe typy zmiennych


Język C++ posiada jeszcze kilka innych wbudowanych typów zmiennych. Można je wygodnie
podzielić na typy całkowite, typy zmiennopozycyjne oraz typy znakowe.
Zmienne zmiennoprzecinkowe zawierają wartości, które można wyrazić w postaci ułamków
dziesiętnych — stanowią obszerny podzbiór liczb rzeczywistych. Zmienne znakowe mają rozmiar
jednego bajtu i są używane do przechowywania 256 znaków i symboli pochodzących z zestawów
znaków ASCII i rozszerzonego ASCII.
Zestaw ASCII jest standardowym zestawem znaków używanych w komputerach. ASCII stanowi
skrót od American Standard Code for Information Interchange. Prawie każdy komputerowy
system operacyjny obsługuje zestaw ASCII, choć wiele systemów obsługuje także inne,
międzynarodowe zestawy znaków.
W tabeli 3.1 przedstawione zostały typy zmiennych używanych w programach C++. Tabela
pokazuje typ zmiennej, jej rozmiar w pamięci (zakładany w tej książce) oraz rodzaj wartości,
jakie mogą być przechowywane w zmiennej takiego typu. Zakres przechowywanych wartości
zależy od rozmiaru zmiennej, więc sprawdź w swoim komputerze wynik działania programu z
listingu 3.1.
Tabela 3.1. Typy zmiennych
Typ Rozmiar Wartości
bool 1 bajt prawda lub fałsz
unsigned short int 2 bajty Od 0 do 65 535
short int 2 bajty Od –32 768 do 32 767
unsigned long int 4 bajty Od 0 do 4 294 967 295
long int 4 bajty Od –2 147 483 648 do 2 147
483 647
int (16 bitów) 2 bajty Od –32 768 do 32 767
int (32 bity) 4 bajty Od –2 147 483 648 do 2 147
483 647
unsigned int (16 bitów) 2 bajty Od 0 do 65 535
unsigned int (32 bity) 4 bajty Od 0 do 4 294 967 295
char 1 bajt 256 różnych znaków
float 4 bajty Od 1.2e-38 do 3.4e38
(dodatnich lub ujemnych)
double 8 bajtów Od 2.2e-308 do 1.8e308
(dodatnich lub ujemnych)

UWAGA Rozmiary zmiennych mogą się różnić od pokazanych w tabeli 3.1 (w zależności od
używanego kompilatora i komputera). Jeśli twój komputer dał taki sam wynik, jaki pokazano pod
listingiem 3.1, tabela 3.1 powinna być zgodna z twoim kompilatorem. Jeśli wynik działania
listingu 3.1 w twoim komputerze jest inny, powinieneś sprawdzić w instrukcji kompilatora jaki
zakres wartości może być przechowywany w zmiennych różnych typów.

Definiowanie zmiennej
Zmienną tworzy się lub definiuje poprzez określenie jej typu, po którym wpisuje się jedną lub
więcej spacji, zaś po nich nazwę zmiennej i średnik. Nazwę zmiennej może stanowić praktycznie
dowolna kombinacja liter, lecz nie może ona zawierać spacji. Poprawnymi nazwami zmiennych są
na przykład: x, J23qrsnf czy myAge. Dobra nazwa zmiennej nie tylko informuje o tym, do czego
jest ona przeznaczona, ale także znacznie ułatwia zrozumienie działania programu. Przedstawiona
poniżej instrukcja definiuje zmienną całkowitą o nazwie myAge:

int myAge;

UWAGA Gdy deklarujesz zmienną, jest dla niej alokowana (przygotowywana i rezerwowana)
pamięć. Wartość zmiennej stanowi to, co w danej chwili znajduje się w tym miejscu pamięci. Za
chwilę zobaczysz, jak można przypisać nowej zmiennej określoną wartość.

W praktyce należy unikać tak przerażających nazw, jak J23qrsnf oraz ograniczyć użycie nazw
jednoliterowych (takich jak x czy i) do zmiennych stosowanych jedynie pomocniczo. Postaraj się
używać nazw opisowych, takich jak myAge (mój wiek) czy howMany (jak dużo). Są one łatwiejsze
do zrozumienia trzy tygodnie po ich napisaniu i nie będziesz łamać sobie głowy nad tym, co
chciałeś osiągnąć pisząc, tę linię kodu.
Przeprowadź taki eksperyment: w oparciu o znajmość kilku pierwszych linii kodu, spróbuj
odgadnąć do, czego on służy:
Przykład 1:
int main()
{
unsigned short x;
unsigned short y;
unsigned short z;
z = x * y;
return 0;
};

Przykład 2:
int main()
{
unsigned short Szerokosc;
unsigned short Dlugosc;
unsigned short Obszar;
Obszar = Szerokosc * Dlugosc;
return 0;
};

UWAGA Jeśli skompilujesz ten program, kompilator ostrzeże cię, że te wartości nie zostały
zainicjalizowane. Wkrótce dowiesz się, jak sobie poradzić z tym problemem.

Oczywiście, łatwiejsze do odgadnięcia jest przeznaczenie drugiego programu, a niedogodność


polegająca wpisywaniu dłuższych nazw zmiennych zostaje z nawiązką nagrodzona (przez łatwość
konserwacji drugiego programu).

Uwzględnianie wielkości liter


Język C++ uwzględnia wielkość liter. Innymi słowy, odróżnia małe i duże litery. Zmienna o
nazwie age różni się od zmiennej Age, która z kolei jest uważana za różną od zmiennej AGE.
UWAGA Niektóre kompilatory umożliwiają wyłączenie rozróżniania dużych i małych liter. Nie daj
się jednak skusić – twoje programy nie będą wtedy działać z innymi kompilatorami, zaś inni
programiści C++ będą mieli wiele problemów z twoim kodem.

Istnieją różne konwencje nazywania zmiennych, i choć nie ma znaczenia, którą z nich przyjmiesz,
ważne jest, by zachować ją w całym kodzie programu. Niespójne nazewnictwo może znacznie
utrudnić zrozumienie twojego kodu przez innych programistów.
Wielu programistów decyduje się na używanie dla swoich zmiennych nazw składających się
wyłącznie z małych liter. Jeśli nazwa wymaga użycia dwóch słów (na przykład „moje auto”),
można zastosować dwie popularne konwencje: moje_auto lub mojeAuto. Druga forma jest
nazywana zapisem wielbłąda, gdyż duża litera w jego środku przypomina nieco garb tego
zwierzęcia.
Niektórzy uważają, że znak podkreślenia (moje_auto) jest łatwiejszy do odczytania, jednak inni
wolą go unikać, gdyż trudniej się go wpisuje. W tej książce będziemy stosować zapis wielbłąda, w
którym wszystkie kolejne słowa będą zaczynać się od wielkiej litery: myCar (mojeAuto),
theQuickBrownFox (szybkiRudyLis), itd.

UWAGA Wielu zaawansowanych programistów korzysta ze stylu zapisu nazywanego notacją


węgierską. Polega ona na poprzedzaniu każdej nazwy zmiennej zestawem znaków opisującym
jej typ. Zmienne całkowite (integer) mogą rozpoczynać się od małej litery „i”, a zmienne typu
long mogą zaczynać się od małej litery „l”. Inne litery oznaczają stałe, zmienne globalne,
wskaźniki, itd. Taki zapis ma dużo większe znaczenie w przypadku języka C, dlatego nie będzie
stosowany w tej książce.

Ten zapis jest nazywany notacją węgierską, ponieważ człowiek, który go wymyślił, Charles
Simonyi z Microsoftu, jest Węgrem. Jego monografię można znaleźć pod adresem
http://www.strangecreations.com/library/c/naming.txt.

Microsoft ostatnio zrezygnował z notacji węgierskiej, zaś zalecenia projektowe dla języka C#
wyraźnie odradzają jej wykorzystanie. Strategię tę stosujemy również w języku C++.

Słowa kluczowe
Niektóre słowa są zarezerwowane przez C++ i nie można używać ich jako nazw zmiennych. Są to
słowa kluczowe, używane do sterowania działaniem programu. Należą do nich if, while, for
czy main. Dokumentacja kompilatora powinna zawierać pełną listę słów kluczowych, ale słowem
kluczowym prawie na pewno nie jest każda sensowna nazwa zmiennej. Lista słów kluczowych
języka C++ znajduje się w dodatku B.

Tak Nie
Definiuj zmienną, zapisując jej typ, a następnie Nie używaj słów kluczowych języka C++ jako
jej nazwę. nazw zmiennych.
Używaj znaczących nazw dla zmiennych. Nie używaj zmiennych bez znaku dla wartości
ujemnych.
Pamiętaj, że język C++ uwzględnia wielkość
znaków.
Zapamiętaj ilość bajtów, jaką każdy typ
zmiennej zajmuje w pamięci oraz jakie wartości
można przechowywać w zmiennych danego
typu.

Tworzenie kilku zmienych jednocześnie


W jednej instrukcji możesz tworzyć kilka zmiennych tego samego typu; w tym celu powinieneś
zapisać typ, a po nim nazwy zmiennych, oddzielone przecinkami. Na przykład:

unsigned int myAge, myWeight; // dwie zmienne typu unsigned int


long int area, width, length; // trzy zmienne typu long

Jak widać, myAge i myWeight są zadeklarowane jako zmienne typu unsigned int. Druga linia
deklaruje trzy osobne zmienne typu long; ich nazwy to area (obszar), width (szerokość) oraz
length (długość). W obrębie jednej instrukcji nie można deklarować zmiennych o różnych
typach.

Przypisywanie zmiennym wartości


Do przypisywania zmiennej wartości służy operator przypisania (=). Na przykład zmiennej Width
przypisujemy wartość 5, zapisując:

unsigned short Width;


Width = 5;

UWAGA Typ long jest skróconym zapisem dla long int, zaś short jest skróconym zapisem
dla short int.

Możesz połączyć te kroki i zainicjalizować zmienną w chwili jej definiowania, zapisując:

unsigned short Width = 5;


Inicjalizacja jest podobna do przypisania, a w przypadku zmiennych całkowitych różnica między
nimi jest niewielka. Później, gdy poznasz stałe, przekonasz się, że pewne wartości muszą być
zainicjalizowane, gdyż nie można im niczego przypisywać. Zasadniczą różnicą między
inicjalizacją a przypisaniem jest to, że inicjalizacja odbywa się w chwili tworzenia zmiennej.
Ponieważ można definiować kilka zmienych jednocześnie, podczas tworzenia można również
inicjalizować więcej niż jedną zmienną. Na przykład:

// Tworzymy dwie zmienne typu long i inicjalizujemy je


long width = 5, length = 7;

W tym przykładzie inicjalizujemy zmienną width typu long, nadając jej wartość 5 oraz zmienną
length tego samego typu, nadając jej wartość 7. Można także mieszać definicje i inicjalizacje:

int myAge = 39, yourAge, hisAge = 40;

W tym przykładzie stworzyliśmy trzy zmienne typu int, inicjalizując pierwszą i trzecią z nich.
Listing 3.2 przedstawia pełny, gotowy do kompilacji program, który oblicza obszar prostokąta i
wypisuje wynik na ekranie.
Listing 3.2. Przykład użycia zmiennych
0: // Demonstracja zmiennych
1: #include <iostream>
2:
3: int main()
4: {
5: using std::cout;
6: using std::endl;
7:
8: unsigned short int Width = 5, Length;
9: Length = 10;
10:
11: // tworzymy zmienną typu unsigned short i inicjalizujemy
12: // ją iloczynem szerokości (Width) i długości (Length)
13: unsigned short int Area = (Width * Length);
14:
15: cout << "Szerokosc:" << Width << "\n";
16: cout << "Dlugosc: " << Length << endl;
17: cout << "Obszar: " << Area << endl;
18: return 0;
19: }

Wynik
Szerokosc:5
Dlugosc: 10
Obszar: 50
Analiza
Linia 1. dołącza wymagany plik nagłówkowy dla biblioteki iostream, dzięki czemu możemy
korzystać z obiektu cout. Linia 3. rozpoczyna program. Linie 5. i 6. definiują cout i endl jako
część przestrzeni nazw standardowych (std).
W linii 8. zdefiniowana jest zmienna całkowita Width typu unsigned sort, która zostaje
zainicjalizowana wartością 5. Definiowana jest także inna zmienna typu unsigned short,
zmienna Length, lecz nie jest ona inicjalizowana. W linii 9. zmiennej Length przypisywana jest
wartość 10.
W linii 13. jest definiowana zmienna całkowita Area typu unsigned short, która jest
inicjalizowana przez wartość uzyskaną w wyniku mnożenia wartości zawartej w zmiennej Width
przez wartość zawartą w zmiennej Length. W liniach od 15. do 17. wartości zmiennych są
wypisywane na ekranie. Zwróć uwagę, że słowo endl powoduje przejście do nowej linii.

typedef
Ciągłe wpisywanie unsigned short int może być żmudne, a co gorsza, może spowodować
wystąpienie błędu. C++ umożliwia użycie słowa kluczowego typedef (od type definition,
definicja typu), dzięki któremu możesz stworzyć skróconą formę takiego zapisu.
Dzięki skróconemu zapisowi tworzysz synonim, lecz zwróć uwagę, że nie jest to nowy typ
(będziesz go tworzyć w rozdziale 6., „Programowanie zorientowane obiektowo”). Przy
zapisywaniu synonimu typu używa się słowa kluczowego typedef, po którym wpisuje się
istniejący typ, zaś po nim nową nazwę typu. Całość kończy się średnikiem. Na przykład:

typedef unsigned short int USHORT;

tworzy nową nazwę typu, USHORT, której można użyć wszędzie tam, gdzie mógłbyś użyć zapisu
unsigned short int. Listing 3.3 jest powtórzeniem listingu 3.2, jednak zamiast typu
unsigned short int została w nim użyta definicja USHORT.

Listing 3.3. Przykład użycia typedef


0: // *****************
1: // Demonstruje użycie słowa kluczowego typedef
2: #include <iostream>
3:
4: typedef unsigned short int USHORT; //definiowane poprzez
typedef
5:
6: int main()
7: {
8:
9: using std::cout;
10: using std::endl;
11:
12: USHORT Width = 5;
13: USHORT Length;
14: Length = 10;
15: USHORT Area = Width * Length;
16: cout << "Szerokosc:" << Width << "\n";
17: cout << "Dlugosc: " << Length << endl;
18: cout << "Obszar: " << Area <<endl;
19: return 0;
20: }
Wynik
Szerokosc:5
Dlugosc: 10
Obszar: 50

UWAGA * oznacza mnożenie.

Analiza
W linii 4. definiowany jest synonim USHORT dla typu unsigned short int. Poza tym program
jest bardzo podobny do programu z listingu 3.2, a wyniki jego działania są takie same.

Kiedy używać typu short, a kiedy typu long?


Jedną z przyczyn kłopotów początkujących programistów C++ jest konieczność wyboru pomiędzy
zadeklarowaniem zmiennej jako wartości typu long lub jako wartości typu short. Reguła jest
bardzo prosta: jeśli istnieje możliwość, że jakakolwiek wartość, która może zostać umieszczona w
zmiennej, przekroczy dozwolony zakres wartości dla danego typu, należy użyć typu o większym
zakresie.
Jak pokazano w tabeli 3.1, zmienne typu unsigned short (zakładając że zajmują dwa bajty)
mogą przechowywać wartości z zakresu od 0 do 65 535, zaś zmienne całkowite signed short
dzielą ten zakres pomiędzy liczby dodatnie a ujemne; stąd maksymalne wartości stanowią w tym
przypadku połowę maksymalnej wartości dla typu unsigned.
Choć zmienne całkowite unsigned long mieszczą bardzo duże liczby (4 294 967 295), w
dalszym ciągu są one znacznie ograniczone. Jeśli potrzebujesz większej liczby, musisz użyć typu
float lub double, zmniejszając jednak precyzję ich przechowywania. Zmienne typu float i
double mogą przechowywać naprawdę bardzo duże wartości, ale w większości komputerów ich
precyzja ogranicza się do 7 lub 9 pierwszych cyfr. Oznacza to, że po tych kilku cyfrach liczba jest
zaokrąglana.
Krótsze zmienne zajmują mniej pamięci. Obecnie pamięć jest tania, więc nie wahaj się używać
typu int, który w twoim komputerze zajmuje najprawdopodobniej cztery bajty.

Zawinięcie liczby całkowitej bez znaku


Zmienne całkowite typu unsigned long mogą pomieścić duże wartości, ale co się stanie, gdy
rzeczywiście zabraknie w nich miejsca?
Gdy typ unsigned int osiągnie swoją maksymalną wartość, „przewija się” i zaczyna od zera,
podobnie jak licznik kilometrów w samochodzie. Listing 3.4 pokazuje, co się dzieje, gdy w
krótkiej zmiennej całkowitej spróbujesz umieścić zbyt dużą wartość.
Listing 3.4. Przykład umieszczenia zbyt dużej wartości w zmiennej całkowitej bez znaku
0: #include <iostream>
1: int main()
2: {
3:
4: using std::cout;
5: using std::endl;
6:
7: unsigned short int smallNumber;
8: smallNumber = 65535;
9: cout << "krotka liczba:" << smallNumber << endl;
10: smallNumber++;
11: cout << "krotka liczba:" << smallNumber << endl;
12: smallNumber++;
13: cout << "krotka liczba:" << smallNumber << endl;
14: return 0;
15: }

Wynik
krotka liczba:65535
krotka liczba:0
krotka liczba:1
Analiza
W linii 7. zmienna smallNumber deklarowana jest jako zmienna typu unsigned short int. W
moim komputerze zmienne tego typu mają dwa bajty i mogą pomieścić wartości od 0 do 65 535.
W linii 8. zmiennej tej jest przypisywana maksymalna wartość, która jest następnie wypisywana w
linii 9.
W linii 10. zmienna smallNumber jest inkrementowana, czyli zwiększana o 1. Symbolem
inkrementacji jest podwójny znak plus (++) (tak jak w nazwie języka C++, co symbolizuje
inkrementację języka C). Tak więc wartością zmiennej smallNumber powinno być teraz 65 536.
Ponieważ jednak zmienne typu unsigned short nie mogą przechowywać wartości większych
od 65 535, wartość ta jest przewijana do 0, które jest wypisywane w linii 11.
W linii 12. zmienna smallNumber jest inkrementowana ponownie, po czym wypisywana jest jej
nowa wartość, czyli 1.

Zawinięcie liczby całkowitej ze znakiem


Liczby całkowite ze znakiem różnią się od liczb całkowitych bez znaku, gdyż połowa wartości,
jakie mogą reprezentować, jest ujemna. Zamiast tradycyjnego samochodowego licznika
kilometrów, możesz wyobrazić sobie zegar podobny do pokazanego na rysunku 3.2. Liczby na
tym zegarze rosną zgodnie z ruchem wskazówek zegara i maleją w kierunku przeciwnym.
Spotykają się na dole tarczy (czyli na godzinie szóstej).

Rys. 3.2. Gdyby zegary stosowały liczby ze znakiem...


W odległości jednej liczby od zera istnieje albo 1 (w kierunku zgodnym z ruchem wskazówek)
albo –1 (w kierunku przeciwnym). Gdy skończą się liczby dodatnie, przejdziesz do największej
liczby ujemnej, a potem z powrotem do zera. Listing 3.5 pokazuje, co się stanie gdy do
maksymalnej liczby dodatniej w zmiennej całkowitej typu short int dodasz 1.
Listing 3.5. Przykład zwiększenia maksymalnej wartości dodatniej w licznie całkowitej ze
znakiem.
0: #include <iostream>
1: int main()
2: {
3: short int smallNumber;
4: smallNumber = 32767;
5: std::cout << "krotka liczba:" << smallNumber << std::endl;
6: smallNumber++;
7: std::cout << "krotka liczba:" << smallNumber << std::endl;
8: smallNumber++;
9: std::cout << "krotka liczba:" << smallNumber << std::endl;
10: return 0;
11: }

Wynik
krotka liczba:32767
krotka liczba:-32768
krotka liczba:-32767
Analiza
W linii 3. zmienna smallNumber deklarowana jest jako zmienna typu signed short int (jeśli
nie wskażemy jawnie, że zmienna jest unsigned, bez znaku, zakłada się że jest signed, ze
znakiem). Program działa bardzo podobnie do poprzedniego, jednak osiągany przez niego wynik
jest całkiem inny. Aby w pełni zrozumieć jego działanie, musisz wiedzieć w jaki sposób liczby
całkowite ze znakiem są reprezentowane bitowo w dwubajtowych zmiennych całkowitych.
Podobnie jak w przypadku liczb całkowitych bez znaku, liczby całkowite ze znakiem po
najwyższej wartości dodatniej przewijają się do najwyższej wartości ujemnej.

Znaki
Zmienne znakowe (typu char) zwykle mają rozmiar jednego bajtu, co wystarczy do przechowania
jednej z 256 wartości (patrz dodatek C). Typ char może być interpretowany jako mała liczba (od
0 do 255) lub jako element zestawu kodów ASCII. Skrót ASCII pochodzi od słów American
Standard Code for Information Interchange. Zestaw znaków ASCII oraz jego odpowiednik ISO
(International Standards Organization) służą do kodowania wszystkich liter (alfabetu łacińskiego),
cyfr oraz znaków przestankowych.

UWAGA Komputery nie mają pojęcia o literach, znakach przestankowych i zdaniach.


Rozpoznają tylko liczby. Zauważają tylko odpowiedni poziom napięcia na określonym złączu
przewodów. Jeśli występuje napięcie, jest ono symbolicznie oznaczane jako jedynka, zaś gdy
nie występuje, jest oznaczane jako zero. Poprzez grupowanie zer i jedynek, komputer jest w
stanie generować wzory, które mogą być interpretowane jako liczby, które z kolei można
przypisywać literom i znakom przestankowym.

W kodzie ASCII mała litera „a” ma przypisaną wartość 97. Wszystkie duże i małe litery,
wszystkie cyfry oraz wszystkie znaki przestankowe mają przypisane wartości pomiędzy 0 a 127.
Dodatkowe 128 znaków i symboli jest zarezerwowanych dla „wykorzystania” przez producenta
komputera, choć standard kodowania stosowany przez firmę IBM stał się niejako „obowiązkowy”.

UWAGA ASCII wymawia się jako „eski.”

Znaki i liczby
Gdy w zmiennej typu char umieszczasz znak, na przykład „a”, w rzeczywistości jest on liczbą
pochodzącą z zakresu od 0 do 255. Kompilator wie jednak, w jaki sposób odwzorować znaki
(umieszczone wewnątrz apostrofów) na jedną z wartości kodu ASCII.
Odwzorowanie litery na liczbę jest umowne; nie ma szczególnego powodu, dla którego mała litera
„a” ma wartość 97. Dopóki zgadzają się na to klawiatura, kompilator i ekran, nie ma żadnych
problemów. Należy jednak zdawać sobie sprawę z dużej różnicy pomiędzy wartością 5 a znakiem
„5.” Ten ostatni ma w rzeczywistości wartość 53, podobnie jak litera „a,” która ma wartość 97.
Ilustruje to listing 3.6.
Listing 3.6. Wypisywanie znaków na podstawie ich kodów
0: #include <iostream>
1: int main()
2: {
3: for (int i = 32; i<128; i++)
4: std::cout << (char) i;
5: return 0;
6: }

Wynik
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEF
GHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl
mnopqrstuvwxyz{|}~_
Ten prosty program wypisuje znaki o wartościach od 32 do 127.

Znaki specjalne
Kompilator C++ rozpoznaje pewne specjalne znaki formatujące. Najpopularniejsze z nich
przedstawia tabela 3.2. Kody te umieszcza się w kodzie programu, wpisując znak odwrotnego
ukośnika, a po nim znak specjalny. Aby umieścić w kodzie znak tabulacji, należy wpisać apostrof,
odwrotny ukośnik, literę „t” oraz zamykający apostrof:

char tabCharacter = '\t';

Ten przykład deklaruje zmienną typu char (o nazwie tabCharacter) oraz inicjalizuje ją
wartością \t, która jest rozpoznawana jako tabulator. Specjalne znaki formatujące używane są do
wypisywania tekstu na ekranie, do zapisu tekstu do pliku lub do innego urządzenia wyjściowego.
Znak specjalny \ zmienia znaczenie znaku, który po nim następuje. Na przykład, normalnie znak
n oznacza po prostu literę n, lecz gdy jest poprzedzony znakiem specjalnym \, oznacza przejście
do nowej linii.
Tabela 3.2. Znaki specjalne
Znak Oznacza
\a Bell (dzwonek)
\b Backspace (znak wstecz)
\f Form feed (koniec strony)
\n New line (nowa linia)
\r Carriage return (powrót „karetki”; powrót na początek linii)
\t Tab (tabulator)
\v Vertical tab (tabulator pionowy)
\' Single quote (apostrof)
\" Double quote (cudzysłów)
\? Question mark (znak zapytania)
\\ Backslash (lewy ukośnik)
\0oo Zapis ósemkowy
\xhhh Zapis szesnastkowy

Stałe
Do przechowywania danych służą także stałe. Jednak w odróżnieniu od zmiennej, jak sama nazwa
sugeruje, wartość stałej nie ulega zmianie. Podczas tworzenia stałej trzeba ją zainicjalizować;
później nie można już przypisywać jej innej wartości.

Literały
C++ posiada dwa rodzaje stałych: literały i stałe symboliczne.
Literał jest wartością wpisywaną bezpośrednio w danym miejscu programu. Na przykład:

int myAge = 39;

myAge jest zmienną typu int; z kolei 39 jest literałem. Literałowi 39 nie można przypisać
wartości, zaś jego wartość nie może ulec zmianie.

Stałe symboliczne
Stała symboliczna jest reprezentowana poprzez swoją nazwę (podobnie jak w przypadku
zmiennych). Jednak w odróżnieniu od zmiennej, po zainicjalizowaniu stałej, nie można później
zmieniać jej wartości.
Jeśli w programie występuje zmienna całkowita o nazwie students (studenci) oraz inna zmienna
o nazwie classes (klasy), możemy obliczyć ilość studentów znając ilość klas (przy zakładając że
w każdej klasie jest piętnastu studentów):

students = classess * 15;

W tym przykładzie, 15 jest literałem. Kod będzie jednak łatwiejszy w czytaniu i w konserwacji,
jeśli zastąpimy literał stałą symboliczną:

students = classess * studentsPerClass;


Jeśli zdecydujesz się zmienić ilość studentów przypadającą na każdą z klas, możesz to zrobić w
definicji stałej studentsPerClass (studentów na klasę), bez konieczności zmiany tej wartości w
każdym miejscu jej wystąpienia.
W języku C++ istnieją dwa sposoby deklarowania stałych symbolicznych. Starszy, tradycyjny
(obecnie uważany za przestarzały) polega na wykorzystaniu dyrektywy preprocesora, #define.

Definiowanie stałych za pomocą #define


Aby zdefiniować stałą w tradycyjny sposób, możesz napisać:

#define studentsPerClass 15

Zwróć uwagę, że stała studentsPerClass nie ma określonego typu (int, char, itd.).
Dyrektywa #define umożliwia jedynie proste podstawianie tekstu. Za każdym razem, gdy
preprocesor natrafia na słowo studentsPerClass, zastępuje je napisem 15.
Ponieważ działanie preprocesora poprzedza działanie kompilatora, kompilator nigdy nie „widzi”
takich stałych; zamiast tego „widzi” po prostu liczbę 15.

Definiowanie stałych za pomocą const


Mimo, że działa instrukcja #define, do definiowania stałych w C++ używa się nowszego,
lepszego sposobu:

const unsigned short int studentsPerClass = 15;

Ten przykład także deklaruje stałą symboliczną o nazwie studentsPerClass, ale tym razem ta
stała ma typ, którym jest unsigned short int. Metoda ta ma kilka zalet, dzięki którym kod
programu jest łatwiejszy w konserwacji i jest bardziej odporny na błędy. Natomiast zdefiniowana
w ten sposób stała posiada typ; dzięki temu kompilator może wymusić użycie jej zgodnie z tym
typem.

UWAGA Stałe nie mogą być zmieniane podczas działania programu. Jeśli chcesz na przykład
zmienić wartość stałej studentsPerClass, musisz zmodyfikować kod źródłowy, po czym
skompilować program ponownie.

TAK NIE
Sprawdzaj, czy liczby nie przekraczają Nie używaj słów kluczowych jako nazw
maksymalnego rozmiaru dopuszczalnego dla zmiennych.
zmiennych całkowitych i czy nie zawijają się do
niewłaściwych wartości.
Nadawaj zmiennym nazwy znaczące, dobrze
odzwierciedlające ich zastosowanie.
Stałe wyliczeniowe
Stałe wyliczeniowe umożliwiają tworzenie nowych typów, a następnie definiowanie ich
zmiennych. Wartości takich zmiennych ograniczają się do wartości określonych w definicji typu.
Na przykład, możesz zadeklarować typ COLOR (kolor) jako wyliczenie, dla którego możesz
zdefiniować pięć wartości: RED, BLUE, GREEN, WHITE oraz BLACK.
Składnię definicji wyliczenia stanowią słowo kluczowe enum, nazwa typu, otwierający nawias
klamrowy, lista wartości oddzielonych przecinkami, zamykający nawias klamrowy oraz średnik.
Oto przykład:

enum COLOR { RED, BLUE, GREEN, WHITE, BLACK };

Ta instrukcja wykonuje dwa zadania:


1. Sprawia, że nowe wyliczenie otrzymuje nazwę COLOR, tj. tworzony jest nowy typ.
2. Powoduje, że RED (czerwony) jest stałą symboliczną o wartości 0, BLUE (niebieski) jest
stałą symboliczną o wartości 1, GREEN (zielony) jest stałą symboliczną o wartości 2, itd.
Każda wyliczana stała posiada wartość całkowitą. Jeśli tego nie określisz, zakłada się że pierwsza
stała ma wartość 0, następna 1, itd. Każda ze stałych może zostać zainicjalizowana dowolną
wartością. Stałe, które nie zostaną zainicjalizowane, będą miały wartości naliczane począwszy od
wartości od jeden większej od wartości stałych zainicjalizowanych. Zatem, jeśli napiszesz:

enum COLOR { RED=100, BLUE, GREEN=500, WHITE, BLACK=700 };

RED będzie mieć wartość 100, BLUE będzie mieć wartość 101; GREEN wartość 500, WHITE (biały)
wartość 501, zaś BLACK (czarny) wartość 700.
Możesz definiować zmienne typu COLOR, ale mogą one przyjmować tylko którąś z wyliczonych
wartości (w tym przypadku RED, BLUE, GREEN, WHITE lub BLACK, albo 100, 101, 500, 501 lub
700). Zmiennej typu COLOR możesz przypisać dowolną wartość koloru. W rzeczywistości możesz
przypisać jej dowolną wartość całkowitą, nawet jeśli nie odpowiada ona dozwolonemu kolorowi;
dobry kompilator powinien w takim przypadku wypisać ostrzeżenie. Należy zdawać sobie sprawę,
że stałe wyliczeniowe to w rzeczywistości zmienne typu unsigned int oraz że te stałe
odpowiadają zmiennym całkowitym. Możliwość nazywania wartości okazuje się bardzo pomocna,
na przykład podczas pracy z kolorami, dniami tygodnia czy podobnymi zestawami. Program
używający typu wyliczeniowego został przedstawiony na listingu 3.7.
Listing 3.7. Przykład stałych wyliczeniowych
0: #include <iostream>
1: int main()
2: {
3: enum Days { Sunday, Monday, Tuesday,
4: Wednesday, Thursday, Friday, Saturday };
5:
6: Days today;
7: today = Monday;
8:
9: if (today == Sunday || today == Saturday)
10: std::cout << "\nUwielbiam weekendy!\n";
11: else
12: std::cout << "\nWracaj do pracy.\n";
13:
14: return 0;
15: }

Wynik
Wracaj do pracy.
Analiza
W linii 3. definiowana jest stała wyliczeniowa Days (dni), posiadająca siedem, odpowiadających
dniom tygodnia, wartości. Każda z tych wartości jest wartością całkowitą, numerowaną od 0 w
górę (tak więc Monday — poniedziałek — ma wartość 1)1.
Tworzymy też zmienną typu Days — tj. zmienną, która będzie przyjmować wartość z listy
wyliczonych stałych. W linii 7. przypisujemy jej wartość wyliczeniową Monday, którą następnie
sprawdzamy w linii 9.
Stała wyliczeniowa zawarta w linii 3. może być zastąpiona serią stałych całkowitych, tak jak
pokazano na listingu 3.8.
Listing 3.8. Ten sam program wykorzystujący stałe całkowite
0: #include <iostream>
1: int main()
2: {
3: const int Sunday = 0;
4: const int Monday = 1;
5: const int Tuesday = 2;
6: const int Wednesday = 3;
7: const int Thursday = 4;
8: const int Friday = 5;
9: const int Saturday = 6;
10:
11: int today;
12: today = Monday;
13:
14: if (today == Sunday || today == Saturday)
15: std::cout << "\nUwielbiam weekendy!\n";
16: else
17: std::cout << "\nWracaj do pracy.\n";
18:
19: return 0;
20: }

Wynik
Wracaj do pracy.
Analiza

1
Amerykanie liczą dni tygodnia zaczynając od niedzieli. — przyp.tłum.
Wynik działania tego programu jest identyczny z wynikiem programu z listingu 3.7. W tym
programie każda ze stałych (Sunday, Monday, itd.) została zdefiniowana jawnie i nie istnieje typ
wyliczeniowy Days. Stałe wyliczeniowe mają tę zaletę, że się same dokumentują —
przeznaczenie typu wyliczeniowego Days jest oczywiste.

You might also like