Zmienne, stałe i komentarze
Program bez pamięci potrafi tylko wypisać z góry ustalony tekst. Zmienne to pudełka, w których program przechowuje dane w trakcie działania — i od nich zaczyna się prawdziwe programowanie. Poznasz też stałe, komentarze i zasięg, czyli zasadę mówiącą, gdzie zmienna w ogóle istnieje.
Czego się dziś nauczysz
- Zadeklarujesz zmienną i przypiszesz jej wartość
- Wyjaśnisz różnicę między deklaracją, inicjalizacją a przypisaniem
- Użyjesz
vari wskażesz, kiedy nie wolno go użyć - Utworzysz stałą i wyjaśnisz, czym różni się od zmiennej
- Opiszesz zasięg zmiennej i wskażesz, gdzie przestaje ona istnieć
- Napiszesz komentarz, który wnosi coś do kodu
Czym jest zmienna
Zmienna to nazwane miejsce w pamięci komputera, w którym program przechowuje jakąś wartość. Wartość można odczytywać i zmieniać — stąd nazwa.
Analogia: pudełko z etykietą
Wyobraź sobie regał z pudełkami. Każde ma etykietę (nazwa zmiennej) i coś w środku (wartość). Etykieta mówi też, co wolno w nim trzymać (typ) — w pudełku podpisanym „liczby całkowite” nie schowasz zdania.
Gdy program potrzebuje wartości, mówi „daj mi zawartość pudełka wiek„. Gdy chce ją zmienić — wkłada do tego samego pudełka coś innego, a stara zawartość przepada.
W C# każda zmienna ma typ, który ustala się raz i już się nie zmienia. Poniżej cztery typy, których użyjemy w tej lekcji; wszystkim przyjrzymy się dokładniej w lekcji 06.
| Typ | Co przechowuje | Przykład |
|---|---|---|
int | liczbę całkowitą | int wiek = 17; |
double | liczbę z częścią ułamkową | double srednia = 4.25; |
string | tekst | string imie = "Anna"; |
bool | prawdę albo fałsz | bool czyZdal = true; |
Kropka, nie przecinek
W kodzie część ułamkową oddziela się kropką: 4.25. Przecinek ma w C# zupełnie inne znaczenie — rozdziela argumenty. Zapis double x = 4,25; to błąd kompilacji.
Uwaga, bo to bywa mylące: program wypisze tę liczbę jako 4,25, bo przy wyświetlaniu obowiązują polskie ustawienia regionalne. W kodzie piszemy kropkę, na ekranie widzimy przecinek.
Deklaracja, inicjalizacja, przypisanie
Trzy słowa, które często myli się ze sobą, a oznaczają trzy różne rzeczy.
int wiek; // DEKLARACJA — mówimy: będzie pudełko na liczbę o nazwie wiek
wiek = 17; // INICJALIZACJA — pierwszy raz coś do niego wkładamy
wiek = 18; // PRZYPISANIE — wymieniamy zawartość na nową
int rok = 2026; // deklaracja i inicjalizacja w jednej linii (tak robi się najczęściej)
| Nazwa | Co się dzieje |
|---|---|
| Deklaracja | Podajemy typ i nazwę. Od tej chwili nazwa jest znana kompilatorowi, ale nie ma jeszcze żadnej wartości. |
| Inicjalizacja | Pierwsze nadanie wartości. |
| Przypisanie | Każda kolejna zmiana wartości. Poprzednia bezpowrotnie znika. |
Zmiennej bez wartości nie wolno użyć
To jedna z ważniejszych rzeczy do zapamiętania na początku:
int wiek;
Console.WriteLine(wiek); // CS0165: Użyto niezainicjowanej zmiennej lokalnej „wiek"
Zwróć uwagę: zmienna nie zawiera zera ani żadnej innej wartości domyślnej. Ona po prostu nie ma jeszcze wartości, a kompilator w ogóle nie pozwoli takiego programu zbudować. To dobra wiadomość — w wielu językach taki kod się uruchomi i wypisze przypadkową liczbę.
Znak = to nie znak równości
W matematyce x = 5 stwierdza fakt. W programowaniu to polecenie: „weź wartość z prawej strony i włóż ją do pudełka po lewej”. Czytaj to jako strzałkę w lewo.
int licznik = 5;
licznik = licznik + 1; // w matematyce bez sensu, w programowaniu: weź 5, dodaj 1, włóż z powrotem
Console.WriteLine(licznik); // 6
Słowo kluczowe var
Jeśli od razu nadajesz zmiennej wartość, kompilator sam potrafi rozpoznać typ. Zamiast pisać go dwa razy, można użyć var.
var imie = "Anna"; // kompilator widzi tekst → string
var wiek = 17; // widzi liczbę całkowitą → int
var srednia = 4.25; // widzi liczbę z kropką → double
var czyZdal = true; // widzi prawda/fałsz → bool
var to NIE jest „zmienna dowolnego typu”
To najczęstsze nieporozumienie. var nie znaczy „typ nieokreślony” — znaczy „kompilatorze, ustal typ sam”. Typ zostaje ustalony raz, na etapie kompilacji, i dalej działa dokładnie tak samo jak wpisany ręcznie.
var wiek = 17; // od tej chwili wiek jest typu int
wiek = "osiemnaście"; // CS0029: Nie można niejawnie przekonwertować typu „string" na „int"
Kiedy var nie zadziała
| Zapis | Dlaczego nie |
|---|---|
var x; | Brak wartości, więc kompilator nie ma z czego odczytać typu. |
var x = null; | null nie mówi nic o typie. |
var jako typ pola klasy | Działa tylko dla zmiennych lokalnych, wewnątrz metod. |
Kiedy używać, a kiedy nie
Używaj var, gdy typ i tak widać z prawej strony: var lista = new List<string>(); — powtarzanie nazwy typu nic nie wnosi.
Wpisuj typ wprost, gdy z prawej strony go nie widać: int liczba = int.Parse(tekst); czyta się lepiej niż var liczba = ..., bo od razu wiadomo, co dostajemy.
W tym kursie w przykładach dla początkujących będziemy zwykle pisać typ wprost — żeby było widać, z czym pracujemy.
Stałe — wartości, które nie mogą się zmienić
Czasem chcemy mieć pewność, że wartość nigdy się nie zmieni: liczba dni w tygodniu, stawka VAT, maksymalna liczba graczy. Służy do tego słowo const.
const int DniWTygodniu = 7;
const double StawkaVat = 0.23;
const string NazwaSzkoly = "ZSMI Lębork";
Console.WriteLine($"Tydzień ma {DniWTygodniu} dni.");
// DniWTygodniu = 8; // CS0131: Lewa strona przypisania musi być zmienną
| Zasada | Wyjaśnienie |
|---|---|
| Wartość od razu | Stałą trzeba zainicjalizować w tej samej linii, w której ją deklarujesz. const int x; to błąd. |
| Wartość znana przy kompilacji | Można napisać const double Pi = 3.14;, ale nie const DateTime Teraz = DateTime.Now; — czas uruchomienia nie jest znany podczas kompilacji. |
| Nazwa jak dla klasy | Stałe nazywamy od wielkiej litery: StawkaVat, a nie stawkaVat. Więcej o tym w następnej lekcji. |
Po co w ogóle stałe, skoro można użyć zwykłej zmiennej?
Z dwóch powodów. Po pierwsze kompilator pilnuje, żeby nikt jej przez pomyłkę nie nadpisał. Po drugie, i ważniejsze — nazwa tłumaczy liczbę. Porównaj:
double doZaplaty = cena * 1.23; — skąd się wzięło 1.23?
double doZaplaty = cena * (1 + StawkaVat); — od razu wiadomo.
Liczby wpisane wprost w kod nazywa się „magicznymi”. Gdy stawka VAT się zmieni, w pierwszej wersji trzeba przeszukać cały program; w drugiej — poprawić jedną linię.
Jak wolno nazywać zmienne
To są zasady techniczne — kompilator ich pilnuje. O tym, jak nazywać, żeby było czytelnie, jest cała następna lekcja.
// Poprawne nazwy
int wiekUcznia = 17;
string imie2 = "Anna";
double _tymczasowa = 1.5;
// Niepoprawne — każda z tych linii to błąd kompilacji
// int 2wiek = 17; nazwa nie może zaczynać się od cyfry
// string imie ucznia; nazwa nie może zawierać spacji
// double cena-netto; nazwa nie może zawierać myślnika
// int class = 5; class to słowo kluczowe języka
| Zasada | Szczegóły |
|---|---|
| Litery, cyfry, podkreślenie | Tylko te znaki. Bez spacji, myślników i znaków działań. |
| Nie zaczyna się od cyfry | liczba1 tak, 1liczba nie. |
| Nie może być słowem kluczowym | class, int, for i pozostałe słowa zarezerwowane przez język są zajęte. |
| Wielkość liter ma znaczenie | wiek, Wiek i WIEK to trzy różne zmienne. To najczęstsze źródło błędu CS0103. |
Polskie znaki: wolno, ale nie rób tego
C# dopuszcza znaki narodowe w nazwach, więc double wartość = 5; się skompiluje. Mimo to w tym kursie nazw z ogonkami nie używamy, i nie powinieneś ich używać w swoim kodzie.
Powody są praktyczne: nazwy z polskimi znakami psują się przy kopiowaniu między systemami o różnym kodowaniu, utrudniają pracę w zespole i sprawiają kłopot przy wyszukiwaniu w kodzie. Piszemy więc wartosc, sredniaOcen, liczbaUczniow — a polskie znaki zostawiamy dla tekstów wyświetlanych użytkownikowi i dla komentarzy.
Komentarze
Komentarz to tekst, który kompilator całkowicie pomija. Jest wyłącznie dla ludzi — dla współpracowników i dla ciebie za pół roku.
// Komentarz jednoliniowy — od dwóch ukośników do końca linii
int cena = 100; // można też dopisać na końcu linii z kodem
/* Komentarz wieloliniowy.
Ciągnie się aż do znaku zamykającego,
więc mieści się w nim dowolnie długi tekst. */
/// <summary>
/// Komentarz dokumentacyjny — trzy ukośniki.
/// Visual Studio pokaże ten opis w podpowiedzi,
/// gdy ktoś najedzie myszą na nazwę metody.
/// </summary>
Skrót, który oszczędza czas
Zaznacz kilka linii i naciśnij Ctrl + K, potem Ctrl + C — Visual Studio zamieni je w komentarz. Ctrl + K, Ctrl + U cofa tę operację. Bardzo przydatne przy szukaniu błędu: wyłączasz fragment kodu, nie kasując go.
Dobry komentarz wyjaśnia DLACZEGO, nie CO
// ✗ Zbędny — kod mówi dokładnie to samo
int wiek = 17; // przypisanie 17 do zmiennej wiek
// ✓ Wnosi coś, czego z kodu nie widać
int wiek = 17; // wiek minimalny do kursu na kat. AM
// ✓ Ostrzeżenie dla następnej osoby
// UWAGA: kolejność ma znaczenie — walidacja musi być przed zapisem
// ✓ Notatka na przyszłość
// TODO: dodać sprawdzanie, czy plik istnieje
Jeśli czujesz, że fragment kodu koniecznie wymaga opisu, bo inaczej nikt go nie zrozumie — często lepszym rozwiązaniem jest zmiana nazw zmiennych albo podzielenie kodu na mniejsze części, niż dopisywanie komentarza.
Zasięg — gdzie zmienna istnieje
Zmienna nie istnieje w całym programie. Żyje tylko wewnątrz bloku, w którym została zadeklarowana — czyli między klamrami { }, w których ją utworzono.
int liczba = 10; // istnieje od tej linii do końca programu
if (liczba > 5)
{
string komunikat = "Duża liczba"; // istnieje tylko w tym bloku
Console.WriteLine(komunikat); // ✓ jesteśmy w środku
Console.WriteLine(liczba); // ✓ blok zewnętrzny też jest widoczny
}
// Console.WriteLine(komunikat); // ✗ CS0103: nazwa nie istnieje w tym kontekście
Zasada jednym zdaniem
Z wnętrza bloku widzisz wszystko na zewnątrz, ale z zewnątrz nie widzisz niczego, co powstało w środku. Gdy program wychodzi z bloku, zadeklarowane w nim zmienne przestają istnieć.
Wygląda to na ograniczenie, ale jest ułatwieniem: dzięki temu tę samą nazwę można spokojnie użyć w różnych miejscach programu, a zmienne pomocnicze nie zaśmiecają reszty kodu.
Kalkulator zakupów
Program liczy wartość zamówienia: cenę netto, podatek i kwotę do zapłaty po rabacie. Wszystko, co poznaliśmy w tej lekcji, w jednym miejscu.
// Stałe — wartości ustalone raz dla całego programu
const double StawkaVat = 0.23; // 23%
const double ProgRabatu = 500.0; // od tej kwoty przysługuje rabat
const double Rabat = 0.10; // 10%
// Dane zamówienia
string nazwaProduktu = "Klawiatura mechaniczna";
int sztuk = 3;
double cenaNetto = 249.99;
// Obliczenia
double wartoscNetto = sztuk * cenaNetto;
double podatek = wartoscNetto * StawkaVat;
double wartoscBrutto = wartoscNetto + podatek;
// Rabat naliczamy tylko powyżej progu
double doZaplaty = wartoscBrutto;
if (wartoscBrutto > ProgRabatu)
{
double kwotaRabatu = wartoscBrutto * Rabat; // istnieje tylko w tym bloku
doZaplaty = wartoscBrutto - kwotaRabatu;
Console.WriteLine($"Przyznano rabat: {kwotaRabatu:F2} zl");
}
Console.WriteLine($"Produkt: {nazwaProduktu}");
Console.WriteLine($"Sztuk: {sztuk}");
Console.WriteLine($"Wartosc netto: {wartoscNetto:F2} zl");
Console.WriteLine($"VAT: {podatek:F2} zl");
Console.WriteLine($"Brutto: {wartoscBrutto:F2} zl");
Console.WriteLine($"DO ZAPLATY: {doZaplaty:F2} zl");
Przyznano rabat: 92,25 zl
Produkt: Klawiatura mechaniczna
Sztuk: 3
Wartosc netto: 749,97 zl
VAT: 172,49 zl
Brutto: 922,46 zl
DO ZAPLATY: 830,22 zl
Omówienie
| Fragment | Dlaczego tak |
|---|---|
const double StawkaVat = 0.23; |
Wartość, która nie ma prawa zmienić się w trakcie działania. Nazwa tłumaczy, czym jest liczba 0,23 — bez niej w kodzie pojawiłaby się „magiczna” liczba. |
double doZaplaty = wartoscBrutto; |
Zmienną deklarujemy przed instrukcją if, bo ma być widoczna także po niej. Gdyby powstała w środku bloku, ostatnia linia programu by się nie skompilowała. |
double kwotaRabatu = ... |
Ta z kolei celowo istnieje tylko w bloku — poza nim jest niepotrzebna. Tak działa zasięg i tak należy z niego korzystać. |
{kwotaRabatu:F2} |
Zapis po dwukropku to format. F2 oznacza „liczba stałoprzecinkowa z dwoma miejscami po przecinku”. Bez tego zobaczylibyśmy 92,2460000000001. |
| Wynik z przecinkiem | W kodzie napisaliśmy 249.99 z kropką, a program wypisał 249,99 z przecinkiem. To nie błąd — przy wyświetlaniu obowiązują ustawienia regionalne systemu. |
Do pieniędzy nie używa się typu double
Ten przykład jest uproszczony dla czytelności. W prawdziwym programie kwoty przechowuje się w typie decimal, bo double potrafi po wielu działaniach zgubić grosz. Dlaczego tak się dzieje, pokażemy w lekcji 06.
Na co uważać
| Kod | Problem |
|---|---|
int wiek; |
CS0165 — użycie zmiennej bez wartości. Zmienna lokalna nie ma wartości domyślnej; kompilator nie pozwoli jej odczytać. |
int Wiek = 17; |
CS0103 — inna wielkość litery na początku. Dla kompilatora to dwie różne nazwy. |
double x = 4,25; |
Przecinek zamiast kropki. W kodzie część ułamkową oddziela kropka. |
int wiek = "17"; |
CS0029 — po prawej jest tekst, po lewej pudełko na liczbę. Cudzysłów robi z liczby napis. |
int liczba = 5; |
CS0128 — zmienna o tej nazwie już istnieje. Za drugim razem nie podajemy typu: samo liczba = 8;. |
const int X; |
Stała musi dostać wartość w linii deklaracji. |
var x; |
Nie ma z czego odczytać typu. var wymaga wartości od razu. |
Pułapka: zmienna zadeklarowana w bloku
To bardzo częsty błąd przy pierwszych programach z instrukcją if:
// ✗ ŹLE
if (punkty > 50)
{
string ocena = "zdany";
}
Console.WriteLine(ocena); // CS0103 — poza blokiem ocena nie istnieje
// ✓ DOBRZE — deklaracja przed blokiem
string ocena = "niezdany";
if (punkty > 50)
{
ocena = "zdany"; // tu tylko zmieniamy wartość
}
Console.WriteLine(ocena); // działa
Zadania
Zadeklaruj cztery zmienne: imię (tekst), wiek (liczba całkowita), średnia ocen (liczba z ułamkiem) i informację, czy jesteś pełnoletni (prawda/fałsz). Wypisz je w czytelnej formie.
pokaż rozwiązanie
string imie = "Anna";
int wiek = 17;
double sredniaOcen = 4.35;
bool czyPelnoletnia = false;
Console.WriteLine($"Imie: {imie}");
Console.WriteLine($"Wiek: {wiek}");
Console.WriteLine($"Srednia ocen: {sredniaOcen}");
Console.WriteLine($"Pelnoletnia: {czyPelnoletnia}");
Zmienna typu bool wypisze się jako True albo False, z wielkiej litery i po angielsku — tak działa metoda zamieniająca wartość logiczną na tekst. Jak to zmienić, poznasz przy instrukcjach warunkowych.
Masz dwie zmienne: int a = 5; i int b = 9;. Zamień ich wartości miejscami tak, aby na końcu a miało 9, a b miało 5. Wypisz wartości przed i po zamianie.
pokaż rozwiązanie
int a = 5;
int b = 9;
Console.WriteLine($"Przed: a = {a}, b = {b}");
int tymczasowa = a; // odkładamy wartość a na bok
a = b; // do a wkładamy wartość b
b = tymczasowa; // do b wkładamy odłożoną wartość
Console.WriteLine($"Po: a = {a}, b = {b}");
Trzecia zmienna jest konieczna. Gdybyś napisał od razu a = b;, pierwotna wartość a zostałaby nadpisana i przepadła bezpowrotnie — nie byłoby już czego włożyć do b.
To klasyczne zadanie na rozmowach o pracę. Warto rozumieć, dlaczego kolejność ma tu znaczenie.
Poniższy program działa, ale jest nieczytelny. Zamień liczby wpisane wprost na nazwane stałe i dodaj komentarze tam, gdzie naprawdę są potrzebne.
double p = 249.0;
double w = p * 0.23;
double c = p + w;
double k = c * 0.9;
Console.WriteLine(k);
pokaż rozwiązanie
const double StawkaVat = 0.23;
const double MnoznikPoRabacie = 0.9; // rabat 10% = zapłata 90% ceny
double cenaNetto = 249.0;
double podatek = cenaNetto * StawkaVat;
double cenaBrutto = cenaNetto + podatek;
double doZaplaty = cenaBrutto * MnoznikPoRabacie;
Console.WriteLine($"Do zaplaty: {doZaplaty:F2} zl");
Ta sama arytmetyka, ale teraz kod tłumaczy się sam. Nazwy p, w, c, k nie mówiły nic — po tygodniu ich autor też by już nie wiedział, co znaczą.
Komentarz jest tylko jeden i jest w miejscu, w którym naprawdę pomaga: wyjaśnia, dlaczego rabat 10% zapisujemy jako mnożnik 0,9. Reszta linii komentarza nie potrzebuje.
Nie uruchamiając programu, zapisz na kartce, co wypisze. Dopiero potem sprawdź w Visual Studio.
int x = 10;
int y = x;
x = 20;
Console.WriteLine(x);
Console.WriteLine(y);
pokaż rozwiązanie
20
10
Instrukcja int y = x; skopiowała wartość, a nie połączyła zmiennych. Powstały dwa niezależne pudełka: jedno z liczbą 10, drugie też z liczbą 10. Późniejsza zmiana x nie ma już żadnego wpływu na y.
Zapamiętaj ten wynik. W bloku o programowaniu obiektowym zobaczysz, że dla obiektów ta sama instrukcja zachowuje się zupełnie inaczej — i będzie to jedno z ważniejszych zaskoczeń w całym kursie.
W poniższym kodzie jest pięć błędów. Znajdź je wszystkie, nazwij i popraw.
const double Pi;
int 2liczba = 5;
double cena = 19,99;
string Imie = "Ala";
Console.WriteLine(imie);
int suma;
Console.WriteLine(suma);
pokaż rozwiązanie
const double Pi = 3.14159; // 1. stała musi dostać wartość od razu
int liczba2 = 5; // 2. nazwa nie może zaczynać się od cyfry
double cena = 19.99; // 3. kropka, nie przecinek
string imie = "Ala"; // 4. mała litera — spójnie z użyciem niżej
Console.WriteLine(imie);
int suma = 0; // 5. zmienna musi mieć wartość przed odczytem
Console.WriteLine(suma);
1. const double Pi; — stałą trzeba zainicjalizować w linii deklaracji.
2. 2liczba — nazwa nie może zaczynać się od cyfry. liczba2 jest poprawne.
3. 19,99 — przecinek jest w C# separatorem argumentów, nie części ułamkowej.
4. Zadeklarowano Imie z wielkiej litery, a odczytano imie z małej — dla kompilatora to dwie różne nazwy, stąd CS0103. Zgodnie z konwencją zmienne lokalne piszemy od małej litery, więc poprawiamy deklarację.
5. int suma; bez wartości, a niżej odczyt — CS0165.
Co trzeba zapamiętać
- Zmienna to nazwane miejsce w pamięci. Ma typ, który ustala się raz i już się nie zmienia.
- Deklaracja tworzy zmienną, inicjalizacja nadaje pierwszą wartość, przypisanie ją zmienia.
- Zmiennej lokalnej bez wartości nie wolno odczytać — to błąd kompilacji CS0165, a nie zero.
- Znak
=to nie równość, tylko polecenie „włóż wartość z prawej do pudełka z lewej”. varkaże kompilatorowi odczytać typ z wartości. Typ i tak jest ustalany raz i na stałe.consttworzy wartość niezmienną — i, co ważniejsze, nadaje liczbie zrozumiałą nazwę.- Wielkość liter ma znaczenie:
wiekiWiekto dwie różne zmienne. - Część ułamkową w kodzie zapisujemy kropką, choć na ekranie zobaczymy przecinek.
- Zmienna żyje tylko w bloku, w którym powstała. Potrzebujesz jej po bloku — zadeklaruj ją przed nim.
- Komentarz ma wyjaśniać dlaczego, a nie powtarzać to, co i tak widać w kodzie.