Перейти к содержимому

Из каких частей состоит программа на C/C++ [2.1]

Из каких частей состоит программа на C/C++ [2.1]

В этом уроке вы увидите, из каких частей состоит программа на C/C++, зачем делить проект на файлы .h и .cpp, как и зачем писать комментарии (включая doxygen), и что делает препроцессор. В конце есть практические задачи и чек-лист самопроверки.

Главная мысль урока: программа на C++ строится из инструкций, выражений и функций. Интерфейс объявляем в заголовках (.h), реализацию пишем в исходниках (.cpp), а препроцессор помогает подключать файлы и управлять сборкой через директивы #include, #define и т.д.

Содержание

1. Цели урока

  • Понять, из чего состоит программа на C++ и как она выполняется.
  • Научиться разделять интерфейс и реализацию (.h и .cpp).
  • Писать понятные комментарии и готовить doxygen-документацию.
  • Применять директивы препроцессора на практике.
  • Собрать простой проект и избежать типичных ошибок.
Что важно запомнить: интерфейс в .h, реализация в .cpp, каждый заголовок защищаем от повторного включения, комментарии объясняют "что" и "зачем", а не переписывают код.
^ К оглавлению

2. Что такое программа на C/C++

Программа на C++ обычно состоит из нескольких файлов. Основные из них: файлы исходного кода (.cpp) и заголовочные файлы (.h). Выполнение программы начинается с функции main(), которая называется точкой входа.

Простое правило: если вы собираете исполняемую программу для ПК, в проекте должен быть main(). Если вы собираете библиотеку, main() обычно не нужен.
#include <iostream>

int main() {
    std::cout << "Hello, robotics!" << std::endl;
    return 0;
}
C++

Программа и запуск -- не одно и то же

Запомните: написать код мало. Его нужно скомпилировать в исполняемый файл, а затем запустить. Успешная компиляция не гарантирует корректную работу -- логические ошибки видны только при запуске и тестировании.

Мини-словарь терминов

ТерминПростое объяснение
Исходный код Текст программы, который пишет человек (файлы .cpp и .h).
Компиляция Перевод исходного кода в объектные файлы (.o/.obj).
Линковка Сборка объектных файлов и библиотек в один исполняемый файл или библиотеку.
Интерфейс То, как другие части программы "видят" ваш код (объявления функций/классов в .h).
Реализация То, как именно это работает (тела функций в .cpp).
Препроцессор Этап до компиляции, который подставляет файлы и заменяет макросы.
^ К оглавлению

3. Инструкции, выражения, функции, библиотеки

3.1. Инструкция (statement)

Инструкция (statement) -- минимальная завершенная команда в C++. Почти все инструкции заканчиваются точкой с запятой ;. Если провести аналогию с текстом, инструкция похожа на законченное предложение.

int a;            // объявление переменной
a = 2;            // присваивание
std::cout << a;   // вывод в консоль
C++

Важные слова из примера: int -- тип данных (целое число), a -- имя переменной, = -- оператор присваивания (кладет значение справа в переменную слева).

3.2. Выражение (expression)

Выражение (expression) -- вычисление, которое дает результат. Выражение может быть частью инструкции.

int a = 2 + 2;              // выражение 2 + 2 дает 4
int b = a + 5;              // выражение a + 5 дает 9
int c = (a + 3) * (b + 1);  // выражение со скобками дает 70
C++
Практика для робота: выражения часто встречаются в расчетах скорости, расстояния, угла, PID-коэффициентов. Ошибка в скобках или порядке операций приводит к неправильному управлению.

3.3. Функция и main()

Функция -- именованный блок кода, который можно вызывать многократно. У функции есть параметры (входные данные) и возвращаемое значение (результат).

main() -- особая функция. Это точка входа программы: с нее начинается выполнение. Если вы пишете программу для ПК, у вас должен быть int main().

#include <iostream>

int sum(int x, int y)
{
    return x + y;
}

int main()
{
    std::cout << sum(2, 3) << std::endl;
    return 0;
}
C++
Зачем функции в робототехнике: выносите повторяющиеся действия в функции. Например: чтение датчика, фильтрация, вычисление скорости, команда моторам. Так проще тестировать и меньше ошибок.

3.4. Библиотека

Библиотека -- это заранее написанный и проверенный код для повторного использования. В C++ часто подключают стандартную библиотеку. Например, <iostream> дает доступ к std::cout.

#include <iostream>  // std::cout, std::cin, std::endl
C++

std:: -- это пространство имен стандартной библиотеки. Оно нужно, чтобы имена не конфликтовали. Например, ваш проект тоже может иметь что-то с именем cout, но std::cout всегда означает объект из стандартной библиотеки.

3.5. Типичные ошибки

Нет точки с запятой

int a = 5  // ошибка: нужен ';'
int b = 6;
C++

Почему так происходит: компилятор читает код слева направо. Если нет ;, он пытается "склеить" строки. В итоге сообщение об ошибке может указывать на следующую строку, хотя причина выше.

Нет функции main()

undefined reference to `main'
Bash

Это ошибка линковщика: он собрал объектные файлы, но не нашел точку входа. Исправление: добавьте int main() в один из .cpp файлов (если вы собираете исполняемый файл).

^ К оглавлению

4. Комментарии и документирование кода

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

В C++ есть два вида комментариев:

// Однострочный комментарий

/* Многострочный
   комментарий */
C++

4.1. Как писать понятно

Хороший комментарий отвечает на вопрос "зачем" или "какой смысл", а не переписывает код. Если комментарий можно удалить и ничего не потеряется, он, скорее всего, лишний.

Плохо

// Присваиваем переменной day значение 0
day = 0;
C++

Дублирует то, что и так видно.

Хорошо

// Датчик света показал темноту -- переходим в ночной режим
day = 0;
C++

Объясняет причину действия.

4.2. Уровни "ЧТО" и "КАК"

  • "ЧТО делает" -- над функцией/в начале файла: назначение, входы, выходы, ограничения.
  • "КАК делается" -- перед сложным местом: кратко про алгоритм, математику, шаги, крайние случаи.
Подсказка: если вы пишете код для робота, в комментариях часто важно указать единицы измерения. Например: "скорость в м/с", "угол в радианах", "время в миллисекундах".

4.3. Doxygen

Doxygen -- инструмент, который создает документацию по коду на основе комментариев. Для doxygen используют блоки вида /** ... */ и теги @brief, @param, @return.

/**
 * @brief Сумма двух целых
 * @param a Первое число
 * @param b Второе число
 * @return a + b
 */
int sum(int a, int b) { return a + b; }
C++

4.4. Ошибки комментирования

  • Дублирование кода словами вместо объяснения смысла.
  • Устаревшие комментарии после правок кода.
  • Слишком много мелких комментариев справа от каждой строки. Лучше один комментарий над блоком.
^ К оглавлению

5. Разделение на .h и .cpp, сборка проекта по шагам

5.1. Что хранить в .h

.h хранит объявления (declarations): прототипы функций, классы, константы. Задача заголовка -- сообщить компилятору и другим .cpp файлам, что существует такая функция/класс и как им пользоваться.

// math_utils.h
#pragma once
int sum(int a, int b);
C++

5.2. Что хранить в .cpp

.cpp хранит определения (definitions): тела функций, реализацию алгоритмов. Здесь находится код, который реально выполняется.

// math_utils.cpp
#include "math_utils.h"
int sum(int a, int b) { return a + b; }

// main.cpp
#include <iostream>
#include "math_utils.h"
int main() {
    std::cout << sum(3, 4) << std::endl; // 7
    return 0;
}
C++

5.3. Объявление и определение

  • Объявление говорит: "объект существует и выглядит так".
  • Определение дает "тело" объекта: реальный код функции или место в памяти под переменную.

Пример

// Объявление (прототип) в .h:
int sum(int a, int b);

// Определение (тело) в .cpp:
int sum(int a, int b) { return a + b; }
C++

Важно: определение должно быть одно, иначе при линковке вы получите ошибку "multiple definition".

5.4. Компиляция и линковка

# Схема сборки (упрощенно)
# .cpp --компилятор--> .o --линковщик--> .exe
Bash
  • Компилятор читает каждый .cpp файл и превращает его в объектный файл (.o).
  • Линковщик объединяет объектные файлы и библиотеки в единый результат (например, .exe), а также проверяет, что для каждого вызова функции есть соответствующее определение.

5.5. Типичные ошибки

Определения в .h

// bad.h
int a = 5; // при подключении в несколько .cpp получите "multiple definition"
C++

Почему это ошибка: заголовок может подключиться сразу в несколько .cpp файлов, и каждый получит свою копию определения. Исправление: в .h только объявления, определения держим в одном .cpp.

Забыли #include

'sum' was not declared in this scope -- компилятор не знает о функции без объявления из заголовка.

Исправление: подключите заголовок, где объявлена функция: #include "math_utils.h".

^ К оглавлению

6. Директивы препроцессора: #include, #define, #ifdef, #pragma once

Препроцессор -- этап обработки текста до компиляции. Он: подключает файлы (#include), заменяет макросы (#define) и может включать или исключать части кода (#ifdef).

6.1. #include

#include подставляет содержимое файла в место директивы. Обычно системные заголовки подключают через <...>, а свои через "...".

#include <iostream>     // системный заголовок
#include "math_utils.h" // ваш заголовок
C++

6.2. #define

#define создает макрос -- правило текстовой замены. Препроцессор заменяет имя макроса на значение. В современных проектах для констант часто используют const и constexpr, но базовое понимание макросов важно, потому что они встречаются в коде и документации.

#define MY_NUMBER 9
std::cout << MY_NUMBER; // станет std::cout << 9;
C++

6.3. #ifdef / #ifndef / #endif

Это условная компиляция. Она позволяет включать код только если макрос определен (или не определен). Частый сценарий: включить отладочный вывод только в debug.

#define PRINT_JOE

#ifdef PRINT_JOE
std::cout << "Joe" << std::endl;
#endif
C++

6.4. #pragma once

#pragma once защищает заголовок от повторного включения. Это помогает избежать ошибок повторного объявления (redefinition).

// header.h
#pragma once
// объявления...
C++

6.5. Ошибки и как их избежать

  • Нет защиты заголовка -- дублирующиеся объявления при множественных include.
  • Слишком много #define для "констант" -- лучше const/constexpr.
  • Скрытая логика через #ifdef без комментариев -- сложно сопровождать.
^ К оглавлению

7. Практика: типовые задачи (с решениями)

Базовые задачи

Задача 1: Hello, Robotics!

Вывести строку в консоль.

#include <iostream>
int main() {
    std::cout << "Hello, Robotics!" << std::endl;
    return 0;
}
C++

Задача 2: Сумма

Напишите функцию и вызовите ее из main().

#include <iostream>

int sum(int a, int b) { return a + b; }

int main() {
    std::cout << sum(10, 25) << std::endl; // 35
    return 0;
}
C++

Файлы и doxygen

Задача 3: Вынесите функцию в .h и .cpp

Сделайте отдельный модуль greet: заголовок и реализацию.

// greet.h
#pragma once
#include <string>
std::string greeting(const std::string& name);

// greet.cpp
#include "greet.h"
std::string greeting(const std::string& name) { return "Hello, " + name + "!"; }
C++

Задача 4: Doxygen-блок

Добавьте doxygen-комментарий к функции.

/**
 * @brief Умножает два целых числа
 * @param a Первый множитель
 * @param b Второй множитель
 * @return Произведение a и b
 */
int multiply(int a, int b) { return a * b; }
C++

Препроцессор и диагностика

Задача 5: Условная компиляция

Сделайте отладочный вывод, который включается через макрос.

#include <iostream>
// #define DEBUG_MODE

int main() {
#ifdef DEBUG_MODE
    std::cout << "Debug mode" << std::endl;
#endif
    std::cout << "Program running" << std::endl;
    return 0;
}
C++

Задача 6: Проверка #pragma once

Создайте заголовок constants.h и подключите его напрямую и через другой заголовок.

// constants.h
#pragma once
const int kRobotSpeed = 50;

// sensors.h
#pragma once
#include "constants.h"

// main.cpp
#include "constants.h"
#include "sensors.h"
#include <iostream>

int main() {
    std::cout << kRobotSpeed << std::endl;
    return 0;
}
C++
^ К оглавлению

8. Чек-лист самопроверки знаний

Отметьте пункты, которые вы действительно понимаете и можете применить без подсказок.

+/-НавыкПроверка
Структура программы Могу перечислить: инструкции, выражения, функции, библиотеки
Точка входа Понимаю, что main() -- точка входа исполняемой программы
Инструкция Знаю, что инструкция обычно заканчивается ; и могу привести пример
Выражение Понимаю, что выражение дает результат и может быть частью инструкции
.h и .cpp Разношу объявления в .h и реализацию в .cpp
Объявление и определение Объясняю разницу и знаю, почему "multiple definition" -- ошибка линковки
Комментарии Пишу комментарии уровня "ЧТО" и "КАК", не дублирую очевидное
Doxygen Умею оформить @brief, @param, @return для функции
Препроцессор Понимаю роль #include и #define и когда применять #ifdef
#pragma once Ставлю защиту в каждый .h и понимаю, от каких ошибок она спасает
^ К оглавлению

Ссылки по теме

Среда, 25 февраля 2026
Из каких частей состоит программа на C/C++ [2.1]