Skompiluj, potem uruchom
Masz już zainstalowany kompilator, więc uruchomienie programu w C++ to cykl w dwóch krokach. W przeciwieństwie do języka skryptowego, w którym interpreter czyta plik linia po linii, C++ najpierw kompiluje kod źródłowy do natywnego pliku wykonywalnego (samodzielnego pliku binarnego z kodem maszynowym), a potem uruchamiasz ten plik bezpośrednio. W czasie działania między twoim programem a procesorem nie stoi żaden interpreter.
Ten podział to powód, dla którego C++ jest szybki, a także dlaczego błąd składni zatrzymuje cię już przy kompilacji, a nie w połowie działania programu. Kompilator sprawdza cały plik, zanim cokolwiek wytworzy.
Każdy samodzielny przykład możesz uruchomić tutaj, na stronie: edytor poniżej kompiluje go i uruchamia za ciebie. Warto jednak wiedzieć, co dzieje się na twoim komputerze, bo tam żyją prawdziwe projekty.
Cykl kompilacji i uruchamiania na twoim komputerze
Załóżmy, że zapisano to jako main.cpp:
#include <iostream>
using namespace std;
int main() {
cout << "Hello from the terminal" << endl;
return 0;
}
Otwórz terminal w folderze z plikiem i wywołaj kompilator. Najpopularniejszy kompilator to g++ (część GCC); clang++ działa tu identycznie:
g++ main.cpp -o main
Jeśli kod skompiluje się bez problemów, g++ nic nie wypisze i utworzy obok źródła nowy plik: plik wykonywalny o nazwie main (albo main.exe na Windowsie). Ten plik to kod maszynowy: nie da się go czytać jak tekstu i jest związany z twoim systemem operacyjnym i procesorem. Teraz go uruchom:
./main
Hello from the terminal
Na Windowsie uruchamiasz go jako main.exe (albo po prostu main) z tego samego terminala. ./ na macOS i Linuksie mówi powłoce "program jest tutaj, w tym folderze, a nie gdzieś w PATH". Początkujący zapominają o tym kroku, a potem dziwią się, że main zwraca command not found.
Co robi -o (i a.out)
Flaga -o nadaje nazwę wynikowi. Pomiń ją, a g++ nadal skompiluje program, ale zapisze plik wykonywalny pod domyślną nazwą: a.out na macOS/Linuksie, a.exe na Windowsie.
g++ main.cpp # produces a.out, not main
./a.out
To ciągle myli ludzi: kompilują, nie widzą błędów, uruchamiają ./main i dostają No such file or directory, bo plik binarny nazywa się w rzeczywistości a.out. Zawsze podawaj -o, aby dokładnie wiedzieć, co uruchamiasz.
Kompilujesz raz po każdej zmianie źródła. Potem plik wykonywalny jest niezależny: możesz uruchomić ./main sto razy bez ponownej kompilacji. g++ wywołujesz ponownie dopiero po edycji pliku .cpp.
Wybór standardu C++
C++ ma wersje: C++11, C++14, C++17, C++20, C++23, a każda dodaje nowe funkcje języka. Haczyk: kompilator wybiera domyślny standard, który może być starszy, niż się spodziewasz, więc nowoczesny kod może się nie kompilować bez oczywistego powodu. Ustaw standard jawnie flagą -std:
g++ -std=c++17 main.cpp -o main
g++ -std=c++20 main.cpp -o main
Oto kod, który używa funkcji z C++17 (structured bindings). W edytorze kompiluje się bez problemu, ale na twoim komputerze wymaga -std=c++17 lub nowszego:
Jeśli zobaczysz błąd w rodzaju 'structured bindings' only available with '-std=c++17', poprawka nie leży w twoim kodzie: wystarczy dodać właściwą flagę -std. W całym tym kursie zakładamy C++17 lub nowszy.
Włącz ostrzeżenia
Program w C++ może się bez problemu skompilować i nadal być błędny. Kompilator, jeśli go poprosisz, wskaże podejrzany kod, zanim ten ugryzie cię w czasie działania. Dodaj -Wall -Wextra:
g++ -std=c++17 -Wall -Wextra main.cpp -o main
Spójrz na ten program. Kompiluje się bez -Wall, ale ma prawdziwy błąd: odczytuje zmienną, której nigdy nie nadano wartości, a to niezdefiniowane zachowanie:
#include <iostream>
using namespace std;
int main() {
int count; // never initialized
cout << count << endl; // reads garbage - undefined behavior
return 0;
}
Z włączonymi ostrzeżeniami kompilator to wychwytuje:
warning: 'count' is used uninitialized [-Wuninitialized]
Wyrób w sobie nawyk kompilowania z -Wall -Wextra od pierwszego dnia. Ostrzeżenia to darmowy przegląd kodu wykonany przez kompilator; ignorowanie ich to sposób, w jaki przeżywają subtelne błędy. Poprawka jest tu prosta: int count = 0;.
Czytanie błędów kompilatora
Gdy g++ odrzuca twój kod, podaje plik, linię i to, co poszło nie tak. Umiejętność czytania tych komunikatów to połowa sukcesu w wychodzeniu z impasu. Oto klasyk, czyli brakujący średnik:
#include <iostream>
using namespace std;
int main() {
cout << "Oops" // no semicolon
return 0;
}
main.cpp:5:5: error: expected ';' before 'return'
5 | return 0;
| ^~~~~~
Zwróć uwagę na kilka rzeczy. Błąd wskazuje linię 5, ale pomyłka jest w linii 4: kompilator orientuje się, że brakuje średnika, dopiero gdy dochodzi do następnego tokenu. Gdy błąd wskazuje linię, która wygląda dobrze, sprawdź linię przed nią. main.cpp:5:5 to plik, linia, a potem kolumna. Popraw tę jedną wskazaną rzecz i skompiluj ponownie, zamiast zgadywać.
Błędy kompilatora oznaczają, że jeszcze nic się nie wykonało. To coś innego niż błędy czasu wykonania, gdy program wystartował, a potem się wysypał. Wyłapywanie pomyłek podczas kompilacji, zanim program w ogóle ruszy, to jedna z największych zalet C++.
Program kontrolny
Uruchom go w edytorze albo zapisz jako main.cpp, wykonaj g++ -std=c++17 -Wall main.cpp -o main, a potem ./main na swoim komputerze. Jeśli pojawią się wszystkie trzy linie, twoje narzędzia działają od początku do końca:
Pojawiają się tu trzy rzeczy, które wkrótce poznasz dokładniej: zmienna int, vector (tablica o zmiennym rozmiarze w C++) i cout do wypisywania. Na razie wystarczy, że program się kompiluje i wypisuje wszystkie trzy linie po kolei.
Dalej: składnia C++
Masz za sobą kompilację i uruchomienie kilku programów, ale do tej pory pomijaliśmy interpunkcję: linie #include, klamry, średniki, int main() i to, dlaczego każda linia wygląda tak, a nie inaczej. Następna strona rozkłada składnię C++ na części, aby struktura przestała wyglądać jak szablonowy kod i zaczęła mieć sens.
Najczęściej zadawane pytania
Jak skompilować i uruchomić program w C++?
Zapisz kod jako main.cpp, otwórz terminal w tym folderze i wykonaj g++ main.cpp -o main, aby utworzyć plik wykonywalny. Następnie uruchom go przez ./main na macOS/Linuksie albo main.exe na Windowsie. Kompilujesz raz; powstały plik możesz uruchamiać dowolnie wiele razy.
Co robi flaga -o w g++?
-o nadaje nazwę plikowi wyjściowemu. g++ main.cpp -o hello tworzy plik wykonywalny o nazwie hello. Bez -o g++ domyślnie używa nazwy a.out (albo a.exe na Windowsie), i dlatego początkujący często uruchamiają plik, o którego istnieniu nie wiedzieli.
Jak skompilować C++ w konkretnym standardzie, np. C++17 lub C++20?
Przekaż flagę -std: g++ -std=c++17 main.cpp -o main albo -std=c++20. Bez niej kompilator używa własnego ustawienia domyślnego, które może być starsze, niż się spodziewasz, więc nowsze funkcje, takie jak structured bindings czy <ranges>, mogą się nie kompilować, dopóki nie ustawisz standardu jawnie.