Menu

Zmienne w C++: deklaracja, inicjalizacja i zasięg

Jak działają zmienne w C++: deklaracja z typem, różnica między przypisaniem a inicjalizacją, inicjalizacja klamrowa, zasady nazewnictwa oraz zasięg, który decyduje, gdzie zmienna żyje i kiedy znika.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Czym jest zmienna

Zmienna to nazwany fragment pamięci, który przechowuje wartość. W C++ każda zmienna ma stały typ, wybierany przy deklaracji, i ten typ nigdy się nie zmienia. To właśnie czyni C++ językiem statycznie typowanym: kompilator zna typ każdej zmiennej, zanim program się uruchomi, i odmawia kompilacji kodu, który zapisuje wartość niewłaściwego rodzaju.

Deklaracja składa się z trzech części: typu, nazwy i (prawie zawsze) wartości początkowej.

Czytaj int age = 30; jako "utwórz int o nazwie age i wstaw do niego 30". Średnik kończy instrukcję, tak jak każdą instrukcję na stronie o komentarzach. Zauważ, że isActive wypisało się jako 1: bool domyślnie wyświetla się jako 1/0, do czego nawiązuje strona o typach danych.

Inicjalizacja a przypisanie

Te dwie rzeczy wyglądają podobnie, ale to różne operacje, a ta różnica to jedna z najważniejszych idei na tej stronie.

Inicjalizacja nadaje zmiennej pierwszą wartość w ramach deklaracji. Przypisanie zmienia wartość zmiennej, która już istnieje.

Możesz zadeklarować zmienną bez inicjalizacji i przypisać wartość później, ale uważaj:

To działa, bo score dostaje wartość przed odczytem. Niebezpieczna wersja to odczyt jako pierwszy, o czym dalej.

Pułapka niezainicjalizowanej zmiennej

To klasyczna pułapka C++, której w wielu innych językach nie ma. Odczyt zmiennej lokalnej, która została zadeklarowana, ale nigdy nie dostała wartości, to niezdefiniowane zachowanie: zmienna zawiera bajty, które akurat były w tej pamięci.

int score;             // niezainicjalizowana
std::cout << score;    // niezdefiniowane zachowanie: wypisze śmieci, "zadziała" albo się wysypie

Kompilator bez problemu to zbuduje. Może się wypisać 0, może 32766, a przy każdym uruchomieniu lub na każdym komputerze zachowanie może być inne, przez co takie błędy bardzo trudno wytropić. Dwie linie obrony:

  • Zawsze inicjalizuj przy deklaracji. int score = 0; nic nie kosztuje i usuwa cały problem.
  • Włącz ostrzeżenia. Kompilacja z -Wall -Wextra sprawia, że kompilator zgłasza wiele odczytów niezainicjalizowanych zmiennych, zanim zdążą zaszkodzić.

Dla każdej zmiennej lokalnej wybieraj pierwszą opcję: nadaj jej sensowną wartość początkową w chwili utworzenia.

Style inicjalizacji: =, () i {}

C++ daje kilka sposobów zapisania wartości początkowej. W większości robią to samo, ale inicjalizacja klamrowa ma jedną dodatkową funkcję bezpieczeństwa, którą warto znać.

Powód, by sięgać po {}, jest taki, że odrzuca konwersje zawężające, czyli przypisania, które po cichu gubiłyby dane. Przy = wartość ułamkowa zostaje po cichu obcięta, a przy {} kompilator cię zatrzyma:

int x = 3.9;    // kompiluje się: x po cichu staje się 3 (.9 jest odrzucane)
int y{3.9};     // błąd kompilacji: zawężanie z double do int jest niedozwolone

Wyłapanie tego w czasie kompilacji to dokładnie to, czego chcesz. Popularny współczesny nawyk to domyślne używanie {} dla tej dodatkowej kontroli i sięganie po = tylko wtedy, gdy inicjalizacja przez kopię czyta się naturalniej.

Zasady i konwencje nazewnictwa

C++ narzuca kilka twardych reguł, a na nie wszyscy nakładają konwencje. Reguły: nazwa może zawierać litery, cyfry i _, nie może zaczynać się od cyfry, nie może być zarezerwowanym słowem kluczowym (jak int czy return) i rozróżnia wielkość liter (age i Age to dwie różne zmienne). Unikaj nazw zaczynających się od podkreślenia i wielkiej litery oraz nazw z dwoma podkreśleniami z rzędu: są zarezerwowane dla implementacji.

Konwencje, których trzyma się większość kodu w C++:

  • Zmienne zapisuje się w snake_case albo camelCase: wybierz jedno i trzymaj się go konsekwentnie, item_count albo itemCount.
  • Nazwy powinny opisywać wartość: count, a nie c; user_email, a nie x.

Czytelne nazwy to nie ozdoba: dzięki nim za kilka miesięcy nadal zrozumiesz ten kod. total_price = item_count * price_per_item tłumaczy się samo, czego t = c * p nigdy nie osiągnie.

Zasięg zmiennej

Zmienna istnieje tylko wewnątrz bloku, czyli { ... }, w którym ją zadeklarowano, i jest niszczona przy klamrze zamykającej. To jej zasięg (scope). Zmienna zadeklarowana wewnątrz pętli lub bloku if jest poza nim niewidoczna:

Zarówno i, jak i square należą do pętli i znikają, gdy ta się kończy, a total jest zadeklarowane w bloku zewnętrznym, więc przetrwa. Blok wewnętrzny może też przesłonić nazwę z bloku zewnętrznego: nowa zmienna o tej samej nazwie tymczasowo ukrywa zewnętrzną, aż blok wewnętrzny się zamknie. To częste źródło zamieszania, więc unikaj ponownego używania nazw w zagnieżdżonych zasięgach.

Wniosek praktyczny: deklaruj każdą zmienną w najmniejszym bloku, który jej potrzebuje, i tam ją inicjalizuj. Wąski zasięg oznacza mniej nazw walczących o twoją uwagę i mniej okazji do odczytu wartości daleko od miejsca, w którym ją ustawiono.

Dalej: typy danych

Każda zmienna na tej stronie zaczynała się od typu: int, double, string, bool. Następna strona szczegółowo omawia typy danych w C++: typy podstawowe i ich rozmiary, liczby całkowite ze znakiem i bez znaku, float a double, char oraz to, jak dobrać właściwy typ do zadania.

Najczęściej zadawane pytania

Jak zadeklarować zmienną w C++?

Napisz typ, potem nazwę, a opcjonalnie nadaj wartość: int age = 30;. Typ (int) pozostaje stały przez całe życie zmiennej, a nazwa (age) służy do odwoływania się do niej. Możesz zadeklarować zmienną bez wartości, int age;, ale zmienna lokalna będzie wtedy zawierać śmieci, dopóki czegoś jej nie przypiszesz, więc zawsze inicjalizuj ją w miejscu deklaracji.

Jaka jest różnica między inicjalizacją a przypisaniem w C++?

Inicjalizacja nadaje zmiennej jej pierwszą wartość w ramach deklaracji: int x = 5; albo int x{5};. Przypisanie zmienia wartość już istniejącej zmiennej: x = 7;. To rozróżnienie ma znaczenie, bo odczyt zmiennej lokalnej, która została zadeklarowana, ale nigdy nie zainicjalizowana, to niezdefiniowane zachowanie.

Co się stanie, jeśli użyjesz niezainicjalizowanej zmiennej w C++?

Odczyt wartości niezainicjalizowanej zmiennej lokalnej to niezdefiniowane zachowanie: zmienna zawiera bajty, które akurat były w tej pamięci, więc program może wypisać losową liczbę, zadziałać przez przypadek albo się wysypać. Kompilator cię nie powstrzyma (choć wiele kompilatorów ostrzega przy -Wall), więc rozwiązaniem jest nadawanie zmiennym lokalnym wartości już przy deklaracji.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ