
Zanim jeszcze przejdziemy do konkretnego pisania kodu, czeka nas lekcja bardzo długa i piekielnie ważna, a pewnie dla większości nudna, ale trzeba się z nią zapoznać dla kontynuowania nauki C++.
Jak wiadomo w komputerze operuje się na danych. A właściwie, czym są te dane w komputerze? Teraz sobie o nich pomówimy. Głównymi miejscami w komputerze gdzie przechowywane są dane to dysk, pamięć i procesor(oczywiście to nie wszystkie, są inne np. pamięć na karcie graficznej itp.). Procesor zostawmy w spokoju, bo kompilator zajmuje się obsługą procesora. O zapisywaniu danych na dysku będzie później.
Teraz o pamięci. Wszystkie dane, z których aktualnie korzysta komputer(programy) znajdują się w pamięci operacyjnej RAM. Przestanę używać słowa "dane", bo to zbyt ogólne pojęcie. Najprostszą daną w programie jest zmienna. Od teraz będę używał słowa "zmienna", bo jest to podstawowy typ danych. Zmienna to na przykład liczba lub literka, którą będziemy używać w naszym programie. Każda zmienna, załóżmy, że mówimy o jakiejś liczbie, np. 35 zajmuje określone miejsce w pamięci. Teraz trochę informacji technicznych. Pamięć w komputerze to po prostu układ zer i jedynek. Może mi nie uwierzysz, ale tak jest. Teraz przedstawię przykładowy układ w pamięci:
00101101 00011011 11111101 01100000 00000000 00000000
Napisałem taki przykładowy układ, żeby było wiadomo, o czym mówię. Każde miejsce zapisać można wartością 0 lub 1, takie miejsce nazywa się bitem. Dla przykładu prędkość łącz internetowych określamy w bitach na sekundę. To właśnie mówi nam o tym ile takich zer lub jedynek przesyłane jest w sekundę. Np. prędkość 192 Kb/s (kilobity na sekundę) oznacza, że 196608 b/s (bitów na sekundę) odbiera lub wysyła(wymienia z serwerem w sieci) nasz komputer na sekundę.
Ale wracając do tematu pamięci, bity poukładane są w bajty. Zauważ, że co 8 bitów zrobiony jest odstęp. Właśnie 8 bitów to 1 bajt. I tak jest podpisana pamięć. Każdy bajt ma swój adres pamięci. Właśnie po adresach procesor odwołuje się do komórek pamięci. Spójrzmy na rysunek:
Widzimy jakiś przykładowy fragment pamięci. Pamięć składa się z komórek, każda komórka to 1 bajt danych, czyli 8 bitów. Dodatkowo każda komórka ma swój adres, czyli po prostu swój numer w kolejności. Procesor ma możliwość zapisywania i odczytywania każdej komórki w pamieci, odwołując się do niej po jej adresie. W ten sposób działa pamięć. Może ciężko sobie to wyobrazić, ale gdy widzimy obrazek na monitorze, to nic innego jak układ zer i jedynek w pamięci.
Może starczy już o bitach i bajtach. Uznajmy, że podstawowe informacje już masz.
Poznałeś juz budowę pamięci RAM. Czas na poznanie terminu zmiennej. Pewnie, gdy czytałeś o budowie pamięci pomyślałeś sobie, że programowanie musi być trudne, skoro będziesz musiał zapisywać dane do komórek pamięci. W rzeczywistości, żeby programować, nawet nie musisz wiedzieć jak działa pamięć. Gdyż istnieje pojęcie zmiennej. Technicznie mówiąc, zmienna to malutki obszar pamięci(najwyżej kilka komórek), gdzie zapisujemy dane. Zmienne mają różne typy. Właściwie każdy język ma inne typy zmiennych, chociaż w większości się pokrywają. Teraz poznamy typy zmiennych występujące w C++ i dowiemy się jak się ich używa. Co prawda jestem przeciwnikiem uczenia się "na pamięć". Uważam, że daną rzecz trzeba zrozumieć, a nie wukwać ja na pamięć, ale niestety typów zmiennych musisz nauczyć się na pamięć, albo zrobić sobie ściągę, z której będziesz korzystał, a z czasem sam automatycznie się ich nauczysz. No to zaczynamy.
int
Jest to zmienna przechowująca liczby całkowite w pamięci. W systemach 32bitowych int zajmuje 4B(bajty), czyli 32 bity. Tzn., że int składa się z 32 kolejnych zer i jedynek, czyli int zajmuje 4 kolejne komórki w pamięci(dane zapisane w 4 komórkach obok siebie). Oto jak przykładowo może wyglądać int w pamięci komputera:
00010011 00101110 10011010 00110100
Taki układ to jakaś z liczb całkowitych, sam nie mam pojęcia jaka. Ile może być takich kombinacji zer i jedynek? Odp.: 2 do potęgi 32, czyli int zawiera 4294967296 możliwych kombinacji liczb. Czy wiesz co to oznacza? Że każda zmienna ma ograniczony zakres przyjmowanych wartości. Zakres int wynosi: od -2147483648 do 2147483647. Nie musisz tego znać na pamięć wystarczy, że będziesz wiedział, że int przyjmuje wartość +-2 miliardy. Jeżeli będziesz w programie potrzebował zmiennej, która będzie musiała przyjmować wartości większe niż 2 miliardy to już będziesz musiał użyć innej zmiennej o większym zakresie.
float
A co jeżeli w programie będziesz musiał użyć, nie liczby całkowitej, tylko ułamka? Z pomocą przychodzi nam zmienna typu float, zmienna przechowująca liczby ułamkowe(tzw. zmiennoprzecinkowe). Float zajmuje 4 bajty(4 komórki pamięci) podobnie jak int.
double
Ta zmienna również przechowuje liczby ułamkowe, jednak zajmuje 8 bajtów, więc jej zakres jest znacznie większy, bo 8 bajtów to 64 bity, czyli ilość możliwych kombinacji równa jest 2 do potęgi 64. To są naprawdę wysokie liczby.
char
Ta zmienna przechowuje liczby całkowite, a zajmuje 1 bajt(jedną komórkę pamięci). Jej zakres jest mały, bo w 1 bajcie można zmieścić tylko 256 kombinacji(2 do potęgi 8), czyli jej zakres to -128 do 127.
Umownie zmienna ta przechowuje także litery, jednak o tym na innej lekcji.
bool
Zmienna ta przechowuje wartość logiczną. Czyli prawda(true) lub fałsz(false). Zajmuje najmniej ile się da, czyli 1 bajt(jedną komórkę). Jej zakres składa się z dwóch wartości true(1) albo false(0). True to po prostu każda wartość niezerowa, a false to wartość 0.
void
Jest to szczególny przypadek, ten typ oznacza, że nie ma określonego typu zmiennej. W zmiennych nie można go stosować, ale gdy będziemy mówić o funkcjach, będziemy go używać.
Przedrostki to wyrazy, które piszemy przed nazwą zmiennej, które zmieniają właściwości zmiennej.
unsigned
Piszemy go przed typami: char i int. Przedrostek ten powoduje, że w zakresie zmiennej nie ma liczb ujemnych, korzystamy tylko z liczb dodatnich. To oddaje 1 bit, który mówi o znaku, więc zakres zmiennej dwukrotnie podwaja się. Np. unsigned int ma zakres od 0 do 4294967296, a unsigned char ma zakres od 0 do 255.
signed
Przeciwieństwo przedrostka unsigned. Powoduje, że zakres przyjmuje tylko wartości ujemne, wydłużając zakres.
long
Piszemy go przed typami: int i double. Przedrostek ten powiększa zmienną o 2 bajty(dwie kolejne komórki), przy czym oczywiście zwiększa się zakres zmiennej. Zmienna long int zajmuje 4+2=6 bajtów, a long double 8+2=10 bajtów.
short
Analogicznie po nazwie jest przeciwieństwem long. Zmniejsza rozmiar zmiennej dla int i double o 2 bajty. short int będzie zajmował 4-2=2 bajty, a short double 8-2=6 bajtów. Nie muszę już chyba wspominać, że zakresy także maleją.
const
Możemy go napisać przed wszystkimi typami zmiennych. Powoduje, że zmienna ma stałą wartość, tzn. nie można zmienić jej wartości podczas działania programu. Wartość nadajemy podczas tworzenia zmiennej, jeżeli tego nie zrobimy to utracimy możliwość nadania zmiennej wartości. Zresztą kompilator uzna to za błąd. Taka zmienna zostaje utworzona w bloku pamięci programu tylko do odczytu.
register
Możemy go także użyć dla dowolnej zmiennej. Powoduje, że zmienna zostaje utworzona i przechowywana w rejestrze procesora, a nie w pamięci. Przyspiesza to czas korzystania ze zmiennej, bo nie trzeba jej kopiować z pamięci do procesora, ponieważ aby procesor mógł operować na zmiennej musi zostać ona przeniesiona do rejestru w procesorze. Powinniśmy korzystać z tego przedrostka tylko, jeżeli bardzo często używamy zmiennej, np. w intensywnej pętli.
extern
Oznacza, że zmienna jest utworzona, w innej jednostce programu, w innym pliku *.cpp naszego projektu. Będzie na ten temat w lekcji o projekcie wielo-plikowym.
volatile
Wątpię żebyś go używał, ale wspomnę. Oznacza on, tyle zmienna ma być częściej kontrolowana przez, program. Nie ma potrzeby go używania w zwykłych programach na komputerze. Jest w pewien sposób odwrotnością const. Osobiście nigdy nie używałem tego przedrostka i dla mnie też jest tylko teorią ;-)
Istnieją jeszcze inne przedrostki, ale tak rzadko używane, że nie będę o nich nawet wspominał. Na razie wystarczy to, co tu jest.
Dosyć tej teorii, przejdźmy do praktyki stosowania zmiennych.
Stwórzmy nowy projekt, bądź usuńmy kod z poprzedniego. Zostawmy czystą postać programu, który nic nie robi:
Tutaj przy okazji jeszcze powiem o komentarzach. Po dwóch znaczkach // rozpoczyna się komentarz, czyli nasze słowa, możemy napisać, co chcemy, a to nie będzie brało udziału w kompilacji, jest to tylko informacja dla programisty. Komentarz rozpoczyna się // a koczy się wraz z końcem linijki. Kompilator będzie ignorował komentarz.
Teraz zajmiemy się kodem. Jak widzisz mamy standardowy szablon programu. Pomiędzy znakami { a } będziemy pisać polecenia dla procesora. Ostatnie polecenie return 0;(mówi, że program wykonał się poprawnie) już masz, więc do roboty.
Załóżmy, że chcemy w programie przechowywać, jakąś liczbę całkowitą. Teraz musimy wybrać odpowiedni typ zmiennej. W naszym przypadku wybieramy int. Co zrobić dalej? Po pierwsze należy zadeklarować zmienną, tzn. utworzyć ją w pamięci. Oznacza to, że będziemy musieli zarezerwować dla naszej zmiennej w pamięci miejsce, czyli dla int, musimy zarezerwować 4 bajty, czyli 4 komórki pamięci obok siebie. Programowanie jest tak proste, że to system wyznacza jakie komórki(o jakich adresach) może zająć nasza zmienna, nas to tak naprawdę w większości wypadków nie interesuje.
Każda zmienna, musi mieć swoją nazwę. W nazwie nie może być polskich znaków, ani spacji, oraz nazwa zmiennej nie może rozpoczynać się od cyfry. Kompilator C++ rozróżnia także duże i małe litery. Oczywiste jest chyba, że nie można powtarzać nazw, zmiennych. Każda musi mieć inną. Przecież kompilator musi jakoś rozróżniać zmienne. Tak deklarujemy zmienną:
Jak widać nie jest to zbyt skomplikowane, wystarczy najpierw napisać typ zmiennej, potem napisać nazwę i zakończyć średnikiem. W ten sposób mamy w pamięci utworzoną zmienną, przechowującą liczbę całkowitą. Niezależnie od tego, co deklarujemy, zasada zawsze jest ta sama, najpierw typ potem nazwa zmiennej. Zadeklarujmy jeszcze kilka zmiennych:
Pewnie zauważyłeś, że typy zmiennych pogrubiają się. W powyższym programie kompilator zarezerwuje miejsce w pamięci dla 5-ciu zmiennych. Dla każdej po kolei. Wykonywanie programu, odbywa się po kolei, od góry do dołu, zatem najpierw kompilator zadeklaruje zmienną LiczbaCalkowita, potem LiczbaUlamkowa i na końcu ZmiennaLogiczna.
A jak deklarować przedrostki? Zasada jest taka, ze przedrostek piszemy przed typem:
Jak widać przedrostki można łączyć po kilka w jednej zmiennej. Dla przykładu zmienna KrotkaNaturalna jest skrócona o 2 bajty, bez znaków ujemnych i utworzona w rejestrze procesora. Taka kombinacja da nam zakres: od 0 do 65536.
Zmienna to obszar w pamięci, ale co nam z samego obszaru? Z samego obszaru niewiele, ale zmienna przyjmuje konkretną wartość. Wartość zmiennej to jakaś kombinacja bitów w jej komórkach, którą może przyjąć. Mając zmienną możemy modyfikować jej wartość, czyli zmieniać wartości bitów w komórkach. Spójrzmy na przykłady. Przypiszmy jakiejś zmiennej konkretną wartość liczbową(czyli jakiś układ bitów).
Jak widać na przykładzie zmiennej LiczbaUlamkowa, aby zmiennej przypisać(nadać) jakąś wartość trzeba po nazwie zmiennej, napisać znak równa się "=" i po nim konkretną wartość, przy czym należy pamiętać, że wartość nie może wykraczać poza zakres zmiennej. Inną rzeczą jest, gdy mamy do czynienia ze zmienną z przedrostkiem const, w trakcie działania programu, nie można zmienić jej wartości. Jej wartość, należy nadać podczas tworzenia zmiennej:
Fachowo ten proces nazywa się inicjalizacją zmiennej. Podczas deklarowania zmiennej, czyli rezerwowania jej w pamięci, od razu przypisujemy w niej określoną wartość dla tej zmiennej. Jeżeli nie zainicjujemy wartości zmiennej podczas tworzenia, to przyjmie ona wartość, jaka aktualnie jest w zapisywanym miejscu pamięci. Innymi słowy, kompilator nawet nie dotknie się do zmiany wartości, tylko zostawi to, co tam było wcześniej, nawet jej nie wyzeruje. Dlatego jeżeli chcemy, żeby zmienna obliczała coś podczas programu i ma robić to od 0, to należy ją od razu wyzerować, czyli zainicjować zerem. Na dobrą sprawę, każdą zmienną przed użyciem powinniśmy zainicjować lub przypisać jej wartość później, bo po utworzeniu nie wiemy, jaką wartość będzie miała.
Dopiero teraz mamy pewność, że zmienna będzie wynosić 0.
Powiemy sobie jeszcze o deklarowaniu typów zmiennych. Załóżmy, że znudziło nam się pisać cały czas long int. Możemy typowi long int nadać swoją nazwę. Służy do tego instrukcja "typedef". Spójrzmy na przykład:
Deklarujemy nowy typ o nazwie liczba, który tak naprawdę będzie typem long int. Najpierw zawsze piszemy instrukcję typedef. Następnie typ, na którym się ma wzorować nasz nowy typ. I dalej nazwę naszego typu. Oczywiście kończymy średnikiem, tak jak każdą inną instrukcję. Jak widać teraz zamiast long int można pisać liczba, czyli używać naszego nowego typu. Będzie on oczywiście oznaczał to samo, co long int. Jak widzisz, nasze typy nie pogrubiają się, co nie znaczy, że ich nie ma. Tylko typy domyślne będą się pogrubiać.
No to chyba tyle będzie w tej lekcji.