Перейти к содержимому
Теория · Первая программа · урок 5 из 7

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

Программа на C++ часто состоит из нескольких .cpp-файлов. Компилятор переводит каждый файл отдельно и, читая один, не видит, что написано в соседнем. Но вызов функции компилируется, только если компилятор уже знает, что такая функция есть. Вот программа из двух файлов: main.cpp вызывает функцию triple, а её тело лежит в triple.cpp.

Файл main.cpp:

C++
#include <iostream>

int triple(int x);

int main() {
    std::cout << triple(14) << '\n';  // 42
}

Файл triple.cpp:

C++
int triple(int x) {
    return x * 3;
}

Компиляцию обоих файлов и их компоновку в одну программу вместе называют сборкой (build).

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

Строка int triple(int x); в main.cpp — это объявление (declaration). Оно повторяет первую строку функции: тип результата, имя и то, что функция принимает, а вместо тела стоит точка с запятой. Объявления компилятору хватает, чтобы проверить вызов triple(14): функция принимает int и возвращает int.

В triple.cpp записано определение (definition) — та же первая строка вместе с телом в фигурных скобках. Объявить функцию можно в каждом файле, который её вызывает, а определить — только в одном.

Кто сообщает об ошибке

Компилятор находит ошибки в тексте одного файла — например, пропущенную точку с запятой, как в программе с Hello, world. По объявлению он заметит и вызов triple(14, 1) с лишним числом. Объектный файл в таком случае не появляется.

Компоновщик видит программу целиком и ищет определение для каждой вызванной функции. Если triple.cpp не попал в сборку, main.cpp скомпилируется без ошибок, а компоновщик остановится:

TEXT
undefined reference to `triple(int)'

Так пишет GNU ld — стандартный компоновщик в Linux. В задачах курса то же сообщение выглядит иначе: undefined symbol: triple(int).

Сообщение компилятора указывает файл и строку, например main.cpp:6, а компоновщик называет функцию, у которой нет определения.

Частая ошибка

Имя в определении должно в точности совпадать с объявленным. Здесь в triple.cpp оно начинается с заглавной буквы:

C++
int Triple(int x) {  // ✗
    return x * 3;
}

Оба файла компилируются без ошибок, но компоновщик снова не находит triple(int). C++ различает заглавные и строчные буквы, поэтому Triple — другая функция, а у объявленной triple определения по-прежнему нет.