Программирование и IT

Си плюс плюс (C++) с нуля

Си плюс плюс — компилируемый язык, на котором пишут там, где важны скорость и прямой доступ к памяти: движки игр, браузеры, базы данных, прошивки. Порог входа выше, чем у Python, но первая работающая программа собирается в первый же вечер.

Обновлено
В этой статье

Что это за язык#

C++ вырос из языка C в начале 1980-х: Бьёрн Страуструп добавил к нему классы,
а потом и остальное, чем язык известен сегодня. Название читается «си плюс
плюс», а два плюса — это шутка авторов: в языке C оператор ++ увеличивает
значение на единицу.

Главное отличие от Python и JavaScript — компиляция. Исходный текст заранее
переводится в машинные инструкции, и получается самостоятельный файл, которому
для запуска не нужен ни интерпретатор, ни виртуальная машина. Отсюда скорость,
отсюда же и строгость: об ошибке в типах ты узнаёшь при сборке, а не во время
работы программы у пользователя.

Второе отличие — память под твоим управлением. Язык не решает за тебя, когда
освобождать занятое. Это даёт контроль над каждым байтом и одновременно
создаёт целый класс ошибок, которых в других языках просто нет.

Язык развивается редакциями стандарта: C++11, C++14, C++17, C++20, C++23.
Современный код на си плюс плюс сильно отличается от учебников девяностых —
ручное выделение памяти, указатели на каждом шагу и массивы в стиле C уступили
место контейнерам и умным указателям. Если в пособии первая же программа
начинается с #include <stdio.h> и void main(), оно устарело.

Где он применяется#

  • Игры. Unreal Engine написан на си плюс плюс, и логика игр в нём — тоже.
  • Браузеры и операционные системы. Движки отрисовки страниц, ядра систем,
    драйверы.
  • Базы данных и высоконагруженные сервисы. Там, где миллисекунда стоит
    денег.
  • Встраиваемая техника. Микроконтроллеры, станки, автомобильная
    электроника. Соседний вход в ту же область — Arduino для
    начинающих: там применяется
    упрощённый C++ и результат сразу виден на плате.
  • Инженерные и графические пакеты. САПР, обработка изображений, звук и
    видео.

Чего на этом языке обычно не делают: простые сайты, скрипты-однодневки,
аналитику данных. Там выигрывают языки, где меньше ручной работы.

Что поставить#

Нужны две вещи: компилятор и редактор.

Компилятор. В Linux почти всегда уже стоит GCC, иначе он ставится пакетным
менеджером (sudo apt install build-essential). В macOS его роль играет Clang
из Command Line Tools — xcode-select --install. В Windows два пути: Visual
Studio Community с набором «Разработка на C++» или MinGW-w64 с компилятором
GCC.

Редактор. Visual Studio Code с официальным расширением C/C++ — бесплатный
и достаточный вариант на любой системе. Visual Studio Community — сильная
бесплатная среда под Windows. CLion удобен, но платный.

Проверить, что всё на месте:

g++ --version

Если версия напечаталась — компилятор найден и можно писать код. Работа в
терминале здесь не прихоть, а обычный режим: команды сборки и запуска
набираются руками, а базовый набор команд разобран в
шпаргалке по Linux.

Первая программа#

Создай файл hello.cpp:

#include <iostream>

int main() {
    std::cout << "Привет, мир!" << std::endl;
    return 0;
}

Что здесь происходит. #include <iostream> подключает средства ввода-вывода.
int main() — функция, с которой начинается выполнение: она обязана быть ровно
одна на всю программу. std::cout — поток вывода, << отправляет в него
значение, std::endl переводит строку. return 0 сообщает системе, что
программа завершилась без ошибок.

Собери и запусти:

g++ -std=c++17 -Wall -Wextra -o hello hello.cpp
./hello

Ключ -o hello задаёт имя готовой программы (в Windows — hello.exe); без
него получится файл a.out. Ключи -Wall -Wextra включают предупреждения —
ставь их с первого дня, компилятор заметит половину твоих ошибок раньше тебя.
-std=c++17 фиксирует редакцию стандарта.

Следующий шаг — ввод и переменные:

#include <iostream>
#include <string>

int main() {
    std::string name;
    int year = 0;

    std::cout << "Имя: ";
    std::cin >> name;
    std::cout << "Год рождения: ";
    std::cin >> year;

    std::cout << name << ", вам примерно " << 2026 - year << " лет\n";
    return 0;
}

Как из текста получается программа#

Сборка идёт в несколько шагов, и знание их названий экономит часы на разборе
ошибок.

  1. Препроцессор подставляет содержимое заголовков вместо строк #include.
  2. Компилятор переводит каждый файл .cpp в объектный файл.
  3. Компоновщик сшивает объектные файлы и библиотеки в готовую программу.

Отсюда два разных сорта ошибок. Ошибка компиляции звучит как «expected ';'» или
«no matching function» — проблема в тексте одного файла. Ошибка компоновки —
«undefined reference to …» — текст в порядке, но реализации функции нет ни в
одном файле, или забыли подключить библиотеку.

Когда файлов становится больше трёх, команды сборки записывают в CMake — и
дальше одна и та же конфигурация собирается на любой системе.

Память и указатели без страшных слов#

Переменная живёт по какому-то адресу. Указатель — переменная, которая хранит
этот адрес.

int x = 5;
int* p = &x;      // p хранит адрес x
*p = 8;           // записать по адресу — то же, что записать в x
std::cout << x;   // 8

Знак & перед именем берёт адрес, звёздочка перед указателем — обращается к
значению по адресу. Ссылка проще: int& r = x; — это второе имя той же
переменной, её нельзя оставить пустой и нельзя перенаправить.

Память бывает двух сортов. Переменные внутри функции лежат на стеке и
уничтожаются сами при выходе из блока. Объекты, созданные через new, лежат в
куче и живут, пока их не удалят.

int* data = new int[100];
// ... работа ...
delete[] data;          // забыли — утечка памяти

Именно в этой паре — источник трёх классических бед: утечка (забыли delete),
двойное освобождение (удалили дважды) и висячий указатель (обратились после
удаления). Современный ответ на все три — не писать new руками:

#include <memory>
#include <vector>

std::vector<int> numbers = {1, 2, 3};
numbers.push_back(4);                 // размер стал 4
auto ptr = std::make_unique<int>(42); // освободится само

std::vector сам выделяет и отдаёт память, std::unique_ptr освобождает
объект, когда выходит из области видимости. Принцип, на котором это держится,
называется RAII: владение ресурсом привязано к времени жизни объекта. Его
стоит понять раньше, чем классы, — он объясняет, почему в аккуратном коде на
си плюс плюс почти не встречается ручного освобождения.

План изучения#

Сроки ниже — ориентир при занятиях по часу в день, а не обещание.

Первый месяц уходит на основы: типы, условия, циклы, функции, массивы и
std::vector, std::string. Критерий готовности — двадцать решённых задач без
подсказок: обработка чисел, строки, простая статистика по массиву.

Второй месяц — структуры и классы: поля, методы, конструктор и деструктор,
разделение на заголовок .h и реализацию .cpp. Здесь же — сборка проекта из
нескольких файлов.

Третий месяц — память и то, что вокруг: указатели, ссылки, передача аргументов
по значению и по ссылке, умные указатели, RAII. Хороший признак усвоения —
умение объяснить, почему копирование большого объекта в аргументе функции
дорого.

Дальше — стандартная библиотека (контейнеры, алгоритмы, итераторы), шаблоны,
а затем выбор направления: движок игры, встраиваемая техника, серверная
разработка. Единого «полного знания» языка не существует, и это нормально.

Частые ошибки новичка#

Указатель без инициализации. int* p; содержит мусор, а запись по нему
роняет программу в случайном месте. Всегда присваивай адрес или nullptr.

Выход за границу массива. У вектора из десяти элементов последний индекс —
девять. Компилятор об этом не предупредит, программа может даже не упасть — и
испортит соседние данные.

= вместо == в условии. if (x = 5) присвоит пятёрку и всегда окажется
истиной. Ключ -Wall про это предупреждает.

Учебник из прошлого века. Код с #include <iostream.h>, void main() и
gets() не соберётся современным компилятором или соберётся с дырами.

Отлаживать удобнее не печатью значений, а отладчиком: g++ -g добавляет
отладочную информацию, дальше gdb ./hello останавливает программу в нужной
строке и показывает значения переменных.

План по этапам

  1. Собрать первую программуПоставить компилятор, написать hello.cpp и собрать его с ключами -Wall -Wextra.
  2. Основы языкаРешить двадцать задач на условия, циклы, функции и вектор — без подсказок и готовых решений.
  3. Свои типыОписать класс с конструктором и деструктором, разнести объявление и реализацию по двум файлам.
  4. ПамятьРазобрать указатели и ссылки, переписать пример с new/delete на std::vector и unique_ptr.
  5. Проект из нескольких файловСобрать программу на 300–500 строк через CMake и запустить её на другой машине.

Начать изучать эту тему у себя

План ляжет в твой репозиторий: отмечай этапы, веди конспект — история изменений покажет, как ты продвинулся.

Начать план

Проверь себя

1.Чему равно x после кода: int x = 5; int* p = &x; *p = *p + 3;

2.Какой ключ g++ задаёт имя готовой программы?

3.С какой функции начинается выполнение программы на C++?

4.Чему равен size() вектора после кода: std::vector<int> v = {1, 2, 3}; v.push_back(4);

Источники

  • cppreferenceПолный справочник по языку и стандартной библиотеке
    бесплатно
  • isocpp.orgСайт комитета по стандарту: руководство по современному стилю и ответы на частые вопросы
    бесплатно
  • Документация GCCКлючи компилятора и расшифровка предупреждений
    бесплатно
  • StepikБесплатные курсы с автопроверкой решений
    бесплатно

Было полезно?

Ещё темы

Программирование и IT Как изучить Python с нуля Python хорош для первого знакомства с программированием: код читается почти как текст, а стандартная библиотека закрывает большинство бытовых задач. Этот план ведёт от установки интерпретатора до собственных скриптов, покрытых тестами, — примерно за четыре месяца при занятиях около часа в день. Программирование и IT Как изучить SQL с нуля SQL — язык запросов к реляционным базам данных. Он нужен разработчикам, аналитикам, тестировщикам и менеджерам, которые хотят сами доставать цифры. Базовые запросы осваиваются за несколько недель, уверенная работа со сложными отчётами — за два-три месяца практики. Ниже — порядок тем и способы тренироваться на настоящей базе. Программирование и IT Как изучить Linux с нуля Linux работает на большинстве серверов, в контейнерах и на множестве устройств, поэтому командная строка нужна разработчику, тестировщику, аналитику и системному администратору. Осваивать систему удобнее всего не чтением списков команд, а ежедневной работой в терминале и решением небольших практических задач. Ниже — последовательность тем на два-три месяца. Программирование и IT Как изучить JavaScript с нуля JavaScript — язык, который исполняется в каждом браузере, а через Node.js ещё и на сервере. У него низкий порог входа — код можно запустить прямо в консоли браузера, — но много неочевидных мест, от приведения типов до асинхронности. План ниже рассчитан на три-четыре месяца и предполагает, что ты уже знаешь основы HTML и CSS или изучаешь их параллельно. Программирование и IT Как изучить Java с нуля Java — строго типизированный язык, на котором написаны банковские системы, серверы крупных сервисов и Android-приложения. Строгость поначалу замедляет, зато компилятор ловит многие ошибки до запуска. Этот план рассчитан примерно на полгода занятий по часу в день и ведёт от первой программы до небольшого серверного приложения. Программирование и IT Как изучить HTML и CSS с нуля HTML описывает, из чего состоит страница, CSS — как она выглядит. Это не языки программирования в строгом смысле, но без них не обходится ни один сайт, и с них удобно начинать путь во фронтенд. Базу вёрстки можно освоить за два-три месяца, если с первой недели собирать настоящие страницы, а не только читать справочник.

Ещё сценарии