Pisanie programów to operowanie nie tylko samymi komendami ale także strukturami potrafiącymi przechowywać wartości obliczane, bądź używane w trakcie działania programy – są to tzw. zmienne i stałe.
Zmienne mogą przyjmować różne wartości w trakcie działania programu, zasada jest jedynie taka, że muszą być one zgodne z typem zmiennej, czyli rodzajem wartości jakie im przypisujemy. Odmienną konstrukcją jest (wartość) stała – przyjmuje ona zdefiniowaną i niezmienną wartością w trakcie działania programu. Tak jak zmiennych używa się do obliczeń w programie, tak stałych używa się do zdefiniowania pewnych punktów wyjścia niezmiennych przez cały program – taką wartością stałą może być np. liczba Pi z przypisaną jej wartością 3.14159.
Deklarację zmiennych zaczynamy od wskazania ich typu – już spotkaliśmy się ze zmiennymi w pierwszym temacie, gdzie omawiane były parametry przekazywane do programu i wartość zwracana na wyjściu programu (tzw. exit code).
Przykłady zmiennych to:
int a123;
char _zn;
float liczbaRzeczywista; … itp.
Przypisanie wartości (inicjalizacja) takim zmiennym może odbywać się w momencie ich deklaracji, lub też już potem w trakcie działania programu, a więc:
a123=17;
double Pi=3.14159;
char a=’A’;
Zasadą jest tutaj także, aby dla zachowania przenoszalności kodu używać jedynie alfabetu łacińskiego bez polskich liter.
Deklaracja zmiennej prócz wymienienia typu wymaga podania jej nazwy. Zasadą jest, że nazwa musi zaczynać się od znaku niebędącego cyfrą, czyli, że np. powyższe nazwy zmiennych są akceptowalne, a poniższe są błędem syntaktycznym:
int 0asKier;
char 20litera;
double 100liter; … itp.
W tych przypadkach preprocesor środowiska zgłosi błąd i uniemożliwi użycie takiej nazwy. Trzeba także pamiętać, że w ramach zasięgu zmiennej (będzie o tym mowa później) nazwy zmiennych muszą być unikalne tzn. nie mogą się powtarzać w programie takie same nazwy dla powoływanych do życia różnych zmiennych.
Istnieją pewne standardy tworzenia nazw zmiennych i tutaj zachęcam, aby zapoznać się z notacją camelCase (wielbłądzią), Hungarian Notation (węgierską) czy też PascalCase (pascalową) – notacje mają uczynić kod programu bardziej czytelnym, a przez to przyjaznym programiście pracującym nad jego rozwojem. Należy pamiętać, że środowisko programistyczne rozróżnia wielkość liter stąd zmienna a nie jest tą samą co A i obie mogą zawierać odrębne wartości.
W przypadku stałych możemy je powołać do życia na dwa sposoby. Pierwszy związany jest z tzw. literałami, czyli ciągami znaków z przypisaną wartością nie będącymi zmiennymi. Literały są widoczne dla programisty, programuje się je na początku programu i używa się ich na przestrzeni całego kodu, jednakże ich użycie przez środowisko jest dosyć ciekawe, bowiem w trakcie przygotowania do kompilowania programu w miejsce literałów są wstawiane… ich wartości. Czyni to preprocesor, czytając cały kod programu zamienia wszystkie literały i w te miejsca niejako “wkłada” ich reprezentację liczbową. Deklaracja takiego literału wygląda następująco:
#define Pi 3.14159
Należy zwrócić uwagę na brak jakiegokolwiek przypisania wartości znakiem = ani też zakończenia linii znakiem ;.
Drugim sposobem powołania do życia stałej jest deklaracja zmiennej nazwijmy to “w sposób klasyczny” i “opieczętowanie” jej deklaracji słowem kluczowym const. Jedynym wymogiem jest tutaj nadanie jej od razu wartości w jednej linii z deklaracją, a więc np.:
const int niezmienna0123=0;
Taka deklaracja stałej nadaje jej od razu wartość, gwarantuje niezmienność tej wartości oraz przypisuje zmiennej typ – co jest czasami szczególnie istotne, gdyż w notacji komputerowej wartość 10 w zmiennej danego typu… nie zawsze równa się wartości 10 w innym typie (będzie o tym jeszcze przy pętlach). Skoro tak, to należy teraz również zauważyć, że zmienna danego typu może przechowywać w sobie wartości wynikające z definicji typu czyli od konkretnej wartości, do konkretnej wartości, ze zbiorem liczb mieszczących się w tych granicach i określanych ich typem tzn. że zmienna należąca do typu liczb całkowitych nie może przyjąć wartości 2.345, a tylko wartości całkowite 2, 3… itp.
Wobec powyższego możemy wydzielić pewien zbiór typów prostych i poszeregować je względem tego jakie wartości mogą przybierać:
Zmienne mogą być typu przechowującego liczby całkowite, liczby rzeczywiste (zmiennoprzecinkowe), znak, tekst, wartości logiczne itp.
Nazwa typu | Zajętość pamięci [B] | Zakres wartości |
bool | 1 | true lub false 0 lub 1 |
char | 1 | -128 ÷ 127 |
unsigned char | 1 | 0 ÷ 255 |
wchar_t | 2 | 0 ÷ 65535 |
short | 2 | -32768 ÷ 32767 -215 ÷ 215-1 |
unsigned short | 2 | 0 ÷ 65535 0 ÷ 216-1 |
int | 4 | -231 ÷ 231-1 |
unsigned int | 4 | 0 ÷ 232-1 |
long | 4 | -231 ÷ 231-1 |
unsigned long | 4 | 0 ÷ 232-1 |
long long | 8 | -263 ÷ 263-1 |
unsigned long long | 8 | 0 ÷ 264-1 |
float | 4 | 3.4E +/- 38 (7 cyfr) |
double | 8 | 1.7E +/- 308 (15 cyfr) |
long double | 8 / 12 / 16 | 1.7E +/- 308 (15 cyfr) |
Pojawia się tutaj słowo: unsigned, powoduje ono, że w danej zmiennej akceptowalne są jedynie wartości nieujemne, wpływa to oczywiście na zakres przechowywanych wartości zgodnie z przedstawioną wyżej tabelą. W przypadku ostatnich, dużych typów zmiennych wielki wpływ na wartości jakie mogą przechowywać mają takie czynniki jak to, czy procesor i system operacyjny danego komputera i użyty kompilator C/C++ są 32, czy 64 bitowe.
Rozmiar zajętości pamięci podany w bajtach [B] to pamięć niezbędna aby zmienną danego typu przechować w komputerze, w pamięci przydzielonej programowi dla jego realizacji. Pamięć ta jest ograniczona, dlatego też należy oszczędnie (w miarę) operować powoływaniem nowych zmiennych, szczególnie tych o dużej zajętości pamięci. Rozmiar jaki rzeczywiście zmienna zajmuje w pamięci możemy sprawdzić komendą sizeof(zmienna); np. jak w poniższym programie:
#include<cstdio> #define Pi 3.14159 int main() { const int _i=0; float sigmaP=45.342f; printf("Zmienna _i o wartości %d zajmuje %d bajty pamięci. \r\n",_i,sizeof(_i)); printf("Zmienna sigmaP o wartości %f zajmuje %d bajty pamięci. \r\n",sigmaP,sizeof(sigmaP)); printf("Zmienna Pi o wartości %d zajmuje %d bajty pamięci. \r\n",Pi,sizeof(Pi)); return 0; }
Wynikiem działania tego programu jest poniższe okienko konsoli:
W zasadzie wyniki są proste do zrozumienia, za wyjątkiem… ostatniej linijki działania programu, gdzie literał Pi będący wartością 3.14159 rzekomo zawiera 8 bajtów pamięci i ma… co najmniej nieoczekiwaną i dziwną wartość. Proponuję zapamiętać ten temat, bo wrócimy do tego później, przy okazji omawiania wskaźników.
Zadania do samodzielnego wykonania:
Pojawiła nam się konstrukcja sizeof(); i należy zapoznać się z nią i sposobem jej używania. Należy także zadeklarować i zainicjować przynajmniej po jednej zmiennej każdego typu i spróbować je wyświetlić za pomocą znajomej komendy printf();Zadanie 1: Napisz program, w którym zadeklarujesz i zainicjalizujesz 10 różnych zmiennych wybranymi przez Ciebie wartościami. Wyświetl je w jednej kolumnie, sformatowanej do prawej. A następnie sprawdź i wyświetl w ten sam sposób ich zajętość pamięci. Uzyskaj jak najbardziej zbliżony do następującego efekt:
Zadanie 2: Zastanów się czy następujące przypisania wartości są poprawne, a następnie sprawdź to samodzielnie w środowisku programistycznym. Co stwierdziłeś? Co się stało?
int a=345; unsigned short b=-1000; char c='A'; float d=6; float e=a; float f=6.456f; double g=12; long double h='Z'-'A'; unsigned int i=7056484739265; long long j='D'+986;
Zadanie 3: Zadeklaruj i zainicjuj zmienną typu bool wartością false i wyświetl ją na konsoli. Co uzyskałeś? Przypisz jej wartość true i ponownie wyświetl. Jaki jest wynik? Spróbuj przypisać do tej zmiennej wartość dowolną inną wartość np. 4 i co uzyskałeś? Wyjaśnij osiągnięte wyniki.
Podsumowanie:
Od tej chwili, aby uważać temat za zaliczony należy znać podstawową strukturę programu, umieć powoływać do życia zmienne, stałe różnych typów, przypisywać im wartości, sprawdzać ich zajętość pamięci oraz wyświetlać te treści w sposób sformatowany zgodnie z potrzebą.