Sesja 8 — Enumy: nazwane liczby zamiast magicznych stałych
✅ Sprawdź się z sesji 7
Cel
Poznać enum i użyć go, żeby zastąpić kody operacji (1, 2, 3…) w maszynie wirtualnej czytelnymi nazwami.
Teoria w pigułce
Problem z “magicznymi liczbami”
case 1:, case 2: w kodzie z poprzednich sesji działa, ale nikt czytający ten kod (łącznie z Wami za miesiąc) nie widzi od razu, co 1 znaczy bez zaglądania do komentarza albo dokumentacji. Takie nieopisane stałe liczbowe nazywa się magicznymi liczbami — to określenie ma lekko negatywny wydźwięk, bo “magia” = “nikt nie wie dlaczego to działa”.
enum — nazwany zbiór stałych całkowitych
enum OpCode {
OP_LOAD = 1,
OP_ADD = 2,
OP_PRINT = 3,
OP_SUB = 4,
OP_MUL = 5,
};Teraz OP_LOAD to po prostu inna nazwa dla liczby 1 — dla kompilatora to wciąż liczba całkowita, ale dla człowieka czytającego kod to coś czytelnego. Jeśli nie podacie wartości jawnie, enum sam ponumeruje kolejno od zera:
enum Kolor { CZERWONY, ZIELONY, NIEBIESKI };
// CZERWONY = 0, ZIELONY = 1, NIEBIESKI = 2 -- automatycznieUżycie w switch
switch (instr.kod) {
case OP_LOAD:
akumulator = instr.wartosc;
break;
case OP_ADD:
akumulator += instr.wartosc;
break;
// ...
}Czytelność case OP_ADD: kontra case 2: to nie kosmetyka — to różnica między kodem, który można zrozumieć za pół roku, a kodem, do którego trzeba dorabiać sobie ściągawkę “co która liczba znaczy”.
W ćwiczeniach CHIP-8 opcode’y są cały czas liczbami hex (0x1000, 0x6000…) — nikt Was nie zmusi do użycia tu enum, ale gdybyście chcieli (rozszerzenie dla chętnych po ćwiczeniu 2), moglibyście zdefiniować enum Chip8Op { OP_JP = 0x1, OP_LD_BYTE = 0x6, ... } i mieć dokładnie tę samą korzyść czytelności. Dokumentacja/materiały do CHIP-8 używają liczb hex wprost, bo tak zwyczajowo opisuje się opcode’y procesorów — ale mechanizm, który dziś poznajecie, stosuje się identycznie.
Zadania
Zadanie 1 — nazwij opkody swojej maszyny
Zdefiniuj enum OpCode z nazwami dla wszystkich pięciu operacji z sesji 5-7 (OP_LOAD, OP_ADD, OP_PRINT, OP_SUB, OP_MUL). Zamień wszystkie case 1: itd. w execute_instruction na case OP_LOAD: itd. Program powinien działać identycznie jak przed zmianą — to jest refaktoryzacja (zmiana formy kodu bez zmiany zachowania), nie nowa funkcjonalność.
Zadanie 2 — pole enum w strukturze
Zmień pole int kod w struct Instrukcja na enum OpCode kod. Zaktualizuj inicjalizację tablicy programu, żeby używała nazw zamiast liczb:
struct Instrukcja program[] = {
{OP_LOAD, 10},
{OP_ADD, 5},
{OP_PRINT, 0},
};Porównaj czytelność z wersją z sesji 7 ({1, 10}) — to ten sam efekt co zamiana caseów w zadaniu 1, tylko po stronie danych zamiast logiki.
Zadanie 3 — nowa operacja przez enum
Dodaj OP_HALT (zatrzymaj wykonywanie programu, nawet jeśli zostały jeszcze instrukcje). W pętli for w main sprawdź warunek zatrzymania i przerwij pętlę instrukcją break; (poza switchem, na poziomie samej pętli — break w C zawsze przerywa najbliższą otaczającą pętlę lub switch, nie oba naraz).
Kryterium sukcesu
- Cały kod używa nazw z
enum OpCodezamiast gołych liczb — zarówno wswitchu, jak i w inicjalizacji tablicy programu. OP_HALTfaktycznie przerywa wykonywanie, zostawiając nieistniejące instrukcje po nim niewykonane.- Potrafisz wytłumaczyć, czym różni się
breakwswitchu odbreakw pętli — i że to dwa osobne użycia tego samego słowa kluczowego.