Co to jest kompilator?
Kompilator to program, który tłumaczy kod źródłowy napisany w języku programowania na kod maszynowy albo na inną postać niższego poziomu, taką jak kod bajtowy, zanim program zostanie uruchomiony. Przetłumaczony program może potem działać bez oryginalnego kodu źródłowego.
Aktualizacja: 24 września 2026
- 1print("Hello")
- 2total = 2 + 3
- 3print(total)
- 4print("Done")
Skompiluj raz, a potem uruchamiaj program, ile razy chcesz: licznik uruchomień rośnie, licznik kompilacji nie. Włącz literówkę, a kompilator odrzuci cały program, więc nic się nie wyświetli.
Zapisz ten program w C jako hello.c:
#include <stdio.h>
int main(void) {
int price = 4;
printf("Total: %d\n", price * 3);
return 0;
}
Potem wpisz gcc hello.c -o hello. Przez chwilę nic widocznego się nie dzieje, a potem obok kodu źródłowego pojawia się nowy plik o nazwie hello. Ten plik to wynik pracy kompilatora: kod maszynowy, który twój procesor może wykonać. Usuń hello.c, a ./hello nadal działa, bo wszystko, czego program potrzebuje, zostało już przetłumaczone.
gcc hello.c -o hello
./hello
Total: 12
Jak działa kompilator krok po kroku
Kompilator czyta kod źródłowy jako zwykły tekst i przetwarza go etapami. Weźmy jedną linię total = price * 3:
- Analiza leksykalna. Tekst jest dzielony na tokeny, czyli "słowa" języka: nazwę
total, operator=, nazwęprice, operator*i liczbę3. Spacje i komentarze są tu odrzucane. - Parsowanie. Tokeny są układane w drzewo składniowe, które zapisuje strukturę: przypisanie, którego wartością jest mnożenie
priceprzez3. Kod, który łamie gramatykę języka, zatrzymuje się tutaj z błędem składni. - Analiza semantyczna. Kompilator sprawdza znaczenie: czy
pricejest zadeklarowane, czy typy są zgodne, czy wywoływana funkcja istnieje. - Optymalizacja. Program jest przepisywany tak, żeby działał szybciej albo zużywał mniej pamięci, bez zmiany tego, co robi.
x * 2może stać się jedną instrukcją przesunięcia, a kod, który nigdy się nie wykona, zostaje usunięty. - Generowanie kodu. Kompilator zapisuje wynik dla wybranego celu: kod maszynowy x86-64 albo ARM albo kod bajtowy dla maszyny wirtualnej.
W C i C++ ostatni krok, linkowanie, łączy twoje skompilowane pliki z kodem bibliotek (na przykład printf ze standardowej biblioteki C) w jeden plik wykonywalny. gcc uruchamia linker za ciebie, dlatego wygląda to na jedno polecenie.
Python udostępnia pierwsze dwa etapy jako standardowe moduły, więc możesz zobaczyć, jak działają na prawdziwych danych:
NAME 'total'
OP '='
NAME 'price'
OP '*'
NUMBER '3'
Assign(
targets=[
Name(id='total', ctx=Store())],
value=BinOp(
left=Name(id='price', ctx=Load()),
op=Mult(),
right=Constant(value=3)))
Na tym drzewie pracuje reszta kompilatora. Nie obchodzą go już spacje ani podziały linii, tylko struktura: przypisz do total wynik mnożenia price przez 3.
Czas kompilacji: błędy znalezione przed uruchomieniem
Ponieważ kompilator czyta cały program, zanim jakakolwiek jego część się wykona, może wcześnie odrzucić błędy. W tym przykładzie nie wykonuje się żadna z trzech linii print w source, nawet pierwsza, bo kompilator Pythona odrzuca tekst, zanim zacznie się wykonanie:
Rejected before running anything:
'(' was never closed (line 3)
Kompilatory języków statycznie typowanych wyłapują więcej niż błędy składni. Ta linia w C, w pliku bad.c, jest poprawna gramatycznie, ale zapisuje tekst w zmiennej całkowitej:
int count = "five";
GCC 14 i nowsze zatrzymują się z błędem:
bad.c:4:17: error: initialization of 'int' from 'char *' makes integer from pointer without a cast [-Wint-conversion]
Ten sam błąd w Pythonie ujawniłby się dopiero przy wykonaniu tej linii. Wyłapywanie go w czasie kompilacji to jeden z powodów, dla których duże zespoły wybierają kompilowane języki ze statycznym typowaniem. Kompilator nie wyłapie jednak wszystkiego: dzielenie przez liczbę, która okazuje się zerem, albo odczyt nieistniejącego pliku nadal kończą się błędem w czasie wykonania.
Przykłady kompilatorów
| Kompilator | Czyta | Tworzy |
|---|---|---|
| GCC, Clang | C, C++ | Kod maszynowy dla x86-64, ARM i innych |
| MSVC (Microsoft Visual C++) | C, C++ | Pliki wykonywalne Windows (.exe, .dll) |
javac | Java | Kod bajtowy JVM (pliki .class) |
rustc | Rust | Kod maszynowy |
go build | Go | Jeden plik wykonywalny |
tsc | TypeScript | JavaScript |
Dwa wiersze pokazują, że kod maszynowy nie jest jedynym możliwym wynikiem. javac tworzy kod bajtowy, który wykonuje wirtualna maszyna Javy, część środowiska uruchomieniowego Javy. tsc zamienia TypeScript na JavaScript, inny język wysokiego poziomu; kompilator, który tak działa, często nazywa się transpilatorem.
Skompilowany kod maszynowy jest związany z jedną rodziną procesorów i jednym systemem operacyjnym. Program skompilowany dla Windowsa na x86-64 nie uruchomi się na Macu z chipem Apple z serii M; ten sam kod źródłowy kompiluje się ponownie dla każdego celu.
Kompilator czy interpreter
Interpreter wykonuje kod źródłowy bezpośrednio, zamiast tworzyć osobny plik z programem, i tak zwykle uruchamia się Pythona i Ruby. Większość nowoczesnych narzędzi językowych łączy oba pomysły: CPython kompiluje twój plik do kodu bajtowego, a potem go interpretuje, a JVM i silnik V8 JavaScriptu kompilują często używany kod do kodu maszynowego w trakcie działania programu. Pełna tabela porównawcza jest na stronie kompilator a interpreter.
Częste nieporozumienia
- "Jeśli się kompiluje, to działa." Kompilator sprawdza, czy program jest poprawny formalnie, a nie czy robi to, co trzeba. Błędy logiczne i awarie w czasie wykonania, takie jak dzielenie przez zero, przechodzą dalej.
- "Python nie jest kompilowany." CPython kompiluje każdy plik do kodu bajtowego przed uruchomieniem. Pliki
.pycw folderach__pycache__to właśnie ten kod bajtowy, zapisany dla modułów importowanych przez program, żeby następne uruchomienie mogło pominąć ten krok. - "Kompilator online to kompilator." Strony, które uruchamiają twój kod w przeglądarce, wysyłają go na serwer, który uruchamia cały zestaw narzędzi (kompilator, linker i sam program) i zwraca wynik. Kompilator to tylko jeden z tych elementów.
- "Skompilowany kod działa wszędzie." Działa na procesorze i systemie operacyjnym, dla których został zbudowany. Przenośność daje ponowna kompilacja albo maszyna wirtualna, taka jak JVM.
Co dalej
Dokumentacja C pokazuje kompilator w codziennym użyciu: jak skompilować i uruchomić program w C prowadzi przez preprocesing, kompilację, asemblację i linkowanie z prawdziwymi poleceniami gcc. Żeby poznać drugi sposób uruchamiania kodu, przeczytaj kompilator a interpreter, a potem środowisko uruchomieniowe, czyli to, co otacza program po jego starcie. Kurs C to dobre miejsce, żeby poćwiczyć z językiem kompilowanym.