Wprowadzenie do zmiennych
W tej lekcji powiemy o tym, jak przechowywać różne wartości w trakcie działania programu, czyli jak korzystać ze zmiennych.
Motywacja
Żeby zrozumieć do czego są one przydatne, napiszmy program, który podnosi pewną liczbę do potęgi trzeciej, a następnie ją wyświetla:
#include <iostream>
int main()
{
std::cout << 212 * 212 * 212;
}
9528128
Jeśli teraz będziemy chcieli podnieść do trzeciej potęgi inną liczbę, będziemy musieli zmieniać trzy miejsca w kodzie. Jest to uciążliwe.
Rozwiązanie
Żeby nie zmieniać za każdym razem tylu rzeczy, załóżmy na chwilę, że liczba, którą podnosimy
do potęgi nazywa się x
. Jak wtedy będzie wyglądał kod?
#include <iostream>
int main()
{
std::cout << x * x * x;
}
Powyższy program na tym etapie, nie przejdzie etapu kompilacji, ponieważ kod źródłowy jeszcze nie jest w pełni poprawny.
Żeby użyć x
w obliczeniu, musimy napierw stworzyć zmienną o tej nazwie:
#include <iostream>
int main()
{
int x = 212;
std::cout << x * x * x;
}
Powyższy program skompiluje się i zadziała poprawnie.
Zmienna to pojemnik na dane określonego typu. Wartość zmiennej może się zmienić, ale jej typ nie.
Tworzenie zmiennych
W wyżej przedstawionym programie zmienną stworzyliśmy w ten sposób:
int x = 212;
Oto schemat tego zapisu:
- 🔢 Z wartością
- ⚪ Bez początkowej wartości
Ten niezwykle istotny zapis składa się z następujących elementów:
Element | Opis |
---|---|
int | typ zmiennej |
x | nazwa zmiennej |
= 212 | inicjalizacja (przypisanie początkowej wartości) |
; | średnik, który kończy instrukcję |
Typ zmiennej
Typ zmiennej mówi o tym, co może dana zmienna przechowywać. Tak jak wspominałem wcześniej, zmienna jest pojemnikiem przechowującym pewne rzeczy, określonego typu.
int
to skrót od angielskiego słowa integer, który oznacza liczbę całkowitą,
czyli taką bez części ułamkowej.
Oprócz użytego wyżej int
dostępnych jest jeszcze wiele innych typów.
Na tym etapie postaraj się zapamiętać następujące typy:
Typ | Opis |
---|---|
int | liczba całkowita |
float | liczba rzeczywista (z częścią ułamkową) |
char | pojedynczy znak |
bool | wartość logiczna, czyli prawda (true ) albo fałsz (false ) |
Wyżej wymienione typy zmiennych zawsze piszemy małą literą. Ma to znaczenie.
Przykłady tworzenia zmiennych:
int
float
char
bool
// Bez inicjalizacji
// Zmienna będzie posiadać przypadkową wartość
int x;
// Tworzymy zmienną i przypisujemy jej wartość 30
int y = 30;
// Bez inicjalizacji
// Zmienna będzie posiadać przypadkową wartość
float x;
// Tworzymy zmienną i przypisujemy jej wartość 30
float y = 30;
// Tworzymy zmienną i przypisujemy jej wartość 1.234
float z = 1.234f;
Gdy zmiennej typu float
przypisujemy wartość z częścią ułamkową,
musimy zakończyć tą liczbę znakiem f
:
- ✔
12.45f
- ❌
12.45
Ma to swoje uzasadnienie, ale o tym kiedy indziej.
// Bez inicjalizacji
// Zmienna będzie posiadać przypadkową wartość
char x;
// Tworzymy zmienną i przypisujemy jej znak 'j'
char y = 'j';
Pojedyncze znaki zapisujemy zawsze między pojedynczymi apostrofami.
- znak:
'c'
- ciąg znaków, składający się z jednego znaku:
"c"
Zatem:
- ✔
char x = 'c';
- ❌
char x = "c";
// Bez inicjalizacji
// Zmienna będzie posiadać przypadkową wartość
bool x;
// Tworzymy zmienną i przypisujemy jej wartość "prawda"
bool y = true;
// Tworzymy zmienną i przypisujemy jej wartość "fałsz"
bool z = false;
Nazwa zmiennej
Nazwa może być dowolna, z pewnymi istotnymi ograniczeniami:
- ❌ nie może posiadać spacji (zabronione: ❌
abc def
) - ❌ nie może zawierać większości specjalnych symboli
- ✔ może składać się z:
- ✔ znaków
a
-z
orazA
-Z
- ✔ cyfr, ale nie na początku (zabronione: ❌
932abc
) - ✔ podkreślnika
_
- ✔ znaków
Dodatkowe uwagi na temat podkreślnika
Podkreślniki są dozwolne w nazwach, jednak są dwa schematy nazewnictwa, które są zarezerwowane przez C++:
- Podkreślnik na początku i duża litera zaraz po (zabronione: ❌
_Speed
) - Podwójny podkreślnik gdziekolwiek: (zabronione: ❌
__xyz
, ❌user__name
)
Możesz utworzyć tyle zmiennych ile tylko chcesz, jednak muszą one różnić się nazwami, bo są ich unikalnymi identyfikatorami (z pewnymi wyjątkami, ale o tym kiedy indziej).
Wielkość liter ma znaczenie:
int x = 30;
float X = 12.34f;
W wyżej przedstawionym kodzie, zmienne x
oraz X
to dwie różne zmienne!
Najlepiej korzystaj z tylko angielskich nazw, czyli np:
float average;
zamiast
float srednia;
Inicjalizacja (wartość początkowa)
Inicjalizacja jest opcjonalnym krokiem w tworzeniu zmiennych. Gdy ją kreujemy, możemy albo zostawić ją bez chcianej wartości:
int x;
lub przypisać jej wartość początkową:
int x = 30;
Uwaga, słowo to: "inicjalizacja" ✔, nie "inicjacja" ❌
Niezainicjowana zmienna, wewnątrz funkcji (np. wewnątrz main
) będzie miała początkowo
nieprzewidywalną wartość, nie zostanie wyzerowana do 0.
Pozostawienie zmiennej bez wartości początkowej może spowodować problemy w dalszej części kodu. Czasami jednak możemy celowo ją pominąć, ale o tym więcej później.
Nigdy nie można czytać ze zmiennej, gdy jest ona w stanie niezainicjowanym!
Średnik na końcu
Tylko dla przypomnienia 😀
int name = 40;
Możliwości
W tej sekcji jedynie pokażę Ci kilka możliwości korzystania ze zmiennych, natomiast ich omówienie znajduje się w następnej lekcji o zmiennych.
Wczytywanie danych od użytkownika
Aby wczytać dane od użytkownika, możemy stworzyć niezainicjializowaną zmienną, a nastęnie powiedzieć C++owi, aby wsadził do niej informacje wpisane przez użytkownika.
#include <iostream>
int main()
{
int age; // aktualny wiek (zmienna niezainicjalizowana!)
std::cout << "Podaj ile masz lat: ";
std::cin >> age;
std::cout << "Za 10 lat będziesz mieć: " << age + 10 << " lat.\n";
}
Rozbijanie skomplikowanych równań
Możemy używać zmiennych do reprezentowania wartości pośrednich w złożonych równaniach. Pomaga to uczynić kod bardziej czytelnym i utrzymywalnym.
#include <iostream>
int main()
{
float current_pos = 10; // w metrach
float target_pos = 20; // w metrach
float current_speed = 2.5f; // w metrach na sekundę
float time_remaining = 0.5f; // w sekundach
// Oblicz końcową pozycję po zakończeniu czasu
// Obliczamy końcową pozycję po zakończeniu czasu
float ending_pos = current_pos + current_speed * time_remaining;
// Oblicz jak daleko od celu się znaleźliśmy
// Obliczamy jak daleko od celu się znaleźliśmy
float error = target_pos - ending_pos;
// Oblicz prędkość potrzebną do wylądowania idealnie w celu
// Obliczamy prędkość potrzebną do wylądowania idealnie w celu
float required_speed = (target_pos - current_pos) / time_remaining;
// Oblicz ile dodatkowej prędkości potrzebowaliśmy, aby wylądować bezpośrednio na celu
// Obliczamy ile dodatkowej prędkości potrzebowaliśmy, aby wylądować bezpośrednio na celu
float extra_speed_needed = required_speed - current_speed;
std::cout << "Wylądowaliśmy " << error << " metrów od celu.\n";
std::cout << "Musielibyśmy lecieć " << extra_speed_needed << "m/s szybciej, aby wylądować bezpośrednio na celu.";
}
We landed 8.75 meters away from the target.
We needed to go 17.5m/s faster to land on the target.
Powtórzenie
Podsumowując, nauczyliśmy się:
- Jak stworzyć zmienną
- Różnic pomiędzy typami zmiennych
- Dlaczego zmienne są przydatne
Dodatkowe wiadomości
Brak inicjalizacji
int main() {
int x; // brak inicjalizacji
}
Niezainicjalizowana zmienna (bez przypisanej początkowej wartości), wewnątrz bloku funkcji (np. wewnątrz main
-a)
będzie na początku posiadała przypadkową wartość, nie będzie wyzerowana.
Zostawienie zmiennej bez wartości początkowej może powodować problemy
w dalszej części kodu. Czasami jednak intencjonalnie możemy to pomijać,
ale o tym kiedy indziej.
Chociaż może się wydawać, że posiadanie możliwości zrobienia takiego błędu w języku jest dziwne, należy pamiętać, że czasem wykorzystuje się to celowo. Otrzymywanie danych wejściowych od użytkownika jest jednym z przypadków kiedy warto zostawić zmienną w niezainicjowanym stanie.
Konwersja typu
Zmienna posiada określony typ, którego nie można dowolnie zmieniać.
Wartości, które w niej spróbujemy zapisać muszą być zgodne z jej typem.
Wiele typów zostanie jednak przekonwertowanych automatycznie.
Możemy na przykład przypisać wartość całkowitą (int
) do zmiennej typu float
(liczba rzeczywista).
Wywołuje to konwersję z int
na float
:
float y = 30; // OK
Przejście w drugą stronę jest nieco bardziej problematyczne. Zmienna typu int
nie może przechowywać części ułamkowej, więc gdy spróbujemy do niej taką wartość
zapisać, zostanie ona skonwertowana, lecz nie wystąpi żadne zaokrąglenie, tylko po prostu ucięcie
części ułamkowej.
C++ nie robi tego przez zaokrąglanie - zamiast tego, obcina po prostu część ułamkową liczby. Na przykład, 3.1415
obcina się do 3
.
int y = 30.5f; // OK, konwersja do 30, ale tracimy część ułamkową
Kompilator analizując kod może wystosować ostrzeżenie, że następuje niejawna konwersja. Niejawna dlatego, że takiego zapisu moglibyśmy użyć nieświadomie i uzyskać nie tą wartość co chcemy.
Staraj się dopasowywać typy, kiedy tylko możesz!
Niejawne konwersje, takie jak float
do int
, powodują utratę informacji!
W najlepszym przypadku może to powodować złe wyniki, w najgorszym nawet katastrofy.
Rakieta Ariane 5 V88 katastrofalnie eksplodowała 37 sekund po starcie z powodu błędnej konwersji z float
na int
, powodując utratę precyzji, która zatrzymała komputer nawigacyjny.