Сначала компиляция, потом запуск
Теперь, когда у вас установлен компилятор, запуск программы на C++ — это цикл из двух шагов. В отличие от скриптового языка, где интерпретатор читает ваш файл строка за строкой, C++ сначала компилирует исходный код в нативный исполняемый файл — самостоятельный бинарник из машинного кода — а затем вы запускаете этот бинарник напрямую. Во время выполнения между вашей программой и процессором нет никакого интерпретатора.
Именно из-за этого разделения C++ быстрый, и именно поэтому синтаксическая ошибка останавливает вас на этапе компиляции, а не на середине выполнения. Компилятор проверяет весь файл, прежде чем что-либо создать.
Любой самодостаточный пример можно запустить прямо здесь, на странице — редактор ниже компилирует и запускает его за вас. Но стоит знать, что происходит на вашей собственной машине, потому что именно там живут настоящие проекты.
Цикл «скомпилировать и запустить» на вашей машине
Допустим, вы сохранили это как main.cpp:
#include <iostream>
using namespace std;
int main() {
cout << "Hello from the terminal" << endl;
return 0;
}
Откройте терминал в папке с файлом и вызовите компилятор. Самый распространённый компилятор — g++ (часть GCC); clang++ работает абсолютно так же для всего, что здесь описано:
g++ main.cpp -o main
Если код компилируется без ошибок, g++ ничего не выводит и создаёт рядом с исходником новый файл: исполняемый файл с именем main (или main.exe на Windows). Этот бинарник — машинный код, не читаемый текст, и он привязан к вашей операционной системе и процессору. Теперь запустите его:
./main
Hello from the terminal
На Windows вы бы запустили его как main.exe (или просто main) из того же терминала. ./ на macOS и Linux говорит оболочке: «программа прямо здесь, в этой папке, а не где-то в PATH» — шаг, который новички забывают, а потом недоумевают, почему main отвечает command not found.
Что делает -o (и a.out)
Флаг -o задаёт имя выходного файла. Опустите его — и g++ всё равно скомпилирует, но запишет исполняемый файл под именем по умолчанию: a.out на macOS/Linux, a.exe на Windows.
g++ main.cpp # produces a.out, not main
./a.out
Это постоянно сбивает людей с толку: они компилируют, не видят ошибок, запускают ./main и получают No such file or directory — потому что бинарник на самом деле называется a.out. Всегда указывайте -o, чтобы точно знать, что вы запускаете.
Вы компилируете один раз на каждое изменение исходника. После этого исполняемый файл независим — можно запускать ./main хоть сто раз без перекомпиляции. Заново вызывать g++ нужно только после того, как вы отредактировали файл .cpp.
Выбор стандарта C++
У C++ есть версии — C++11, C++14, C++17, C++20, C++23 — каждая добавляет возможности в язык. Подвох в том, что ваш компилятор выбирает стандарт по умолчанию, который может быть старее, чем вы ожидаете, поэтому современный код может не скомпилироваться без видимой причины. Задайте стандарт явно через -std:
g++ -std=c++17 main.cpp -o main
g++ -std=c++20 main.cpp -o main
Вот код, использующий возможность C++17 (structured bindings). В редакторе он компилируется без проблем, но на вашей машине ему нужен -std=c++17 или новее:
Если вы когда-нибудь увидите ошибку вроде 'structured bindings' only available with '-std=c++17', решение не в вашем коде — оно в том, чтобы добавить правильный флаг -std. На протяжении всего этого курса считайте, что у вас C++17 или новее.
Включите предупреждения
Программа на C++ может скомпилироваться без ошибок и всё равно быть неправильной. Компилятор, если его попросить, укажет на подозрительный код раньше, чем тот укусит вас во время выполнения. Добавьте -Wall -Wextra:
g++ -std=c++17 -Wall -Wextra main.cpp -o main
Посмотрите на эту программу. Без -Wall она компилируется, но в ней есть реальная ошибка — чтение переменной, которой никогда не присваивалось значение, а это неопределённое поведение:
#include <iostream>
using namespace std;
int main() {
int count; // never initialized
cout << count << endl; // reads garbage - undefined behavior
return 0;
}
С включёнными предупреждениями компилятор отмечает это:
warning: 'count' is used uninitialized [-Wuninitialized]
Возьмите за привычку компилировать с -Wall -Wextra с самого первого дня. Предупреждения — это бесплатное код-ревью от компилятора; игнорировать их — значит позволять тонким ошибкам выживать. Здесь исправление — просто int count = 0;.
Чтение ошибок компилятора
Когда g++ отвергает ваш код, он называет файл, строку и что именно пошло не так. Умение читать эти сообщения — половина дела, чтобы выбраться из тупика. Вот классика — пропущенная точка с запятой:
#include <iostream>
using namespace std;
int main() {
cout << "Oops" // no semicolon
return 0;
}
main.cpp:5:5: error: expected ';' before 'return'
5 | return 0;
| ^~~~~~
Несколько вещей, на которые стоит обратить внимание. Ошибка указывает на строку 5, но ошибка — в строке 4: компилятор понимает, что точки с запятой не хватает, лишь дойдя до следующего токена. Поэтому, когда ошибка указывает на строку, которая выглядит нормально, проверьте строку выше. main.cpp:5:5 — это файл, строка, затем столбец. Исправьте ровно то одно, что он называет, и перекомпилируйте — не поддавайтесь желанию гадать.
Ошибки компилятора означают, что ничего ещё не запускалось. Они отличаются от ошибок времени выполнения, когда программа стартовала, а потом упала. Ловить ошибки на этапе компиляции, до того как программа вообще запустится, — одна из главных сильных сторон C++.
Программа-проверка
Запустите это в редакторе или сохраните как main.cpp и выполните g++ -std=c++17 -Wall main.cpp -o main, затем ./main на своей машине. Если появились все три строки, ваш набор инструментов работает от начала до конца:
Здесь появляются три вещи, с которыми вы как следует познакомитесь совсем скоро: переменная int, vector (массив C++ с изменяемым размером) и cout для вывода. Пока достаточно того, что программа компилируется и печатает все три строки по порядку.
Далее: синтаксис C++
Вы скомпилировали и запустили несколько программ, но мы пробежались мимо пунктуации — строк #include, фигурных скобок, точек с запятой, int main() и того, почему каждая строка выглядит именно так. Следующая страница разбирает синтаксис C++ по частям, чтобы структура перестала казаться шаблоном и начала обретать смысл.
Часто задаваемые вопросы
Как скомпилировать и запустить программу на C++?
Сохраните код как main.cpp, откройте терминал в этой папке и выполните g++ main.cpp -o main, чтобы получить исполняемый файл. Затем запустите его командой ./main на macOS/Linux или main.exe на Windows. Компилируете вы один раз; полученный бинарник можно запускать сколько угодно раз.
Что делает флаг -o в g++?
-o задаёт имя выходного файла. g++ main.cpp -o hello создаёт исполняемый файл с именем hello. Если опустить -o, g++ по умолчанию использует имя a.out (или a.exe на Windows) - именно поэтому новички часто запускают файл, о создании которого даже не подозревали.
Как скомпилировать C++ под конкретный стандарт, например C++17 или C++20?
Передайте флаг -std: g++ -std=c++17 main.cpp -o main или -std=c++20. Без него компилятор берёт собственный стандарт по умолчанию, который может быть старее, чем вы ожидаете, поэтому новые возможности вроде structured bindings или <ranges> могут не компилироваться, пока вы не зададите стандарт явно.