Сборка падает: undefined reference — как найти и исправить ошибку

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

«Undefined reference» — главный враг новичка в C++ (и не только)

Вы пишете код, всё выглядит идеально, компилятор молчит, вы нажимаете «Собрать» — и тут… стена красного текста. Главный герой этого кошмара — undefined reference (или unresolved external symbol). Знакомо? Если вы хоть раз писали на C++, то наверняка встречались с этой ошибкой. Она пугает новичков, но на самом деле это просто сигнал: «Я знаю, о какой функции ты говоришь, но не могу найти её реализацию».

В этом туториале мы разберём, что такое ошибка неопределённой ссылки, почему она возникает и, самое главное, — как её исправить. Вы узнаете 5 самых частых причин и получите готовый чек-лист для устранения проблемы. Поехали!

Что такое undefined reference / unresolved external symbol?

Начнём с теории, но совсем чуть-чуть. Процесс сборки C++ программы состоит из двух этапов:

  • Компиляция — каждый .cpp файл превращается в объектный файл (.o или .obj). Компилятор проверяет синтаксис и запоминает, какие функции объявлены (прототипы).
  • Линковка (связывание) — сборщик (linker) соединяет все объектные файлы и библиотеки в единый исполняемый файл. На этом этапе он ищет реализации всех обещанных функций.

Ошибка undefined reference (в Visual Studio — unresolved external symbol) возникает именно на этапе линковки. Компилятор прошёл успешно, но линковщик не нашёл тело функции. Он кричит: «Ты сказал, что функция foo() существует, но я не могу найти, где она определена!».

Топ-5 причин ошибки undefined reference в C++

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

1. Вы объявили, но не определили функцию

Классика. Вы написали прототип функции, но забыли реализацию.

// main.cpp
#include <iostream>

void hello(); // объявление (прототип)

int main() {
    hello();
    return 0;
}
// Реализация hello() отсутствует!

Ошибка линковки: undefined reference to `hello()'. Исправление: допишите тело функции.

2. Вы не подключили файл с реализацией к сборке

Это частая проблема при работе с несколькими .cpp файлами. Допустим, вы объявили функцию в utils.h, определили в utils.cpp, но забыли добавить utils.cpp в команду компиляции.

// utils.h
void printMessage();

// utils.cpp
#include <iostream>
#include "utils.h"
void printMessage() {
    std::cout << "Hello!" << std::endl;
}

// main.cpp
#include "utils.h"
int main() {
    printMessage();
    return 0;
}

Если вы скомпилируете только main.cpp (g++ main.cpp), линковщик не увидит utils.cpp. Исправление: скомпилируйте оба файла вместе: g++ main.cpp utils.cpp -o program.

3. Класс или шаблон определён в .cpp, а не в .h

Это специфическая боль C++. Если вы определяете метод класса прямо в .cpp файле, но подключаете только заголовок, линковщик не найдёт реализацию.

// MyClass.h
class MyClass {
public:
    void doSomething();
};

// MyClass.cpp
#include "MyClass.h"
void MyClass::doSomething() { /* ... */ }

// main.cpp
#include "MyClass.h"
int main() {
    MyClass obj;
    obj.doSomething(); // Ошибка: undefined reference
    return 0;
}

Исправление: убедитесь, что MyClass.cpp участвует в сборке (см. пункт 2).

4. Неправильная линковка библиотек (например, math.h)

Некоторые библиотеки (как математическая libm) требуют явного указания при линковке. Если вы используете sqrt() или sin(), но не подключаете -lm, получите ошибку.

// main.cpp
#include <cmath>
int main() {
    double x = sqrt(25.0);
    return 0;
}

Компиляция без флага: g++ main.cpp → ошибка undefined reference to `sqrt'. Исправление: g++ main.cpp -lm (флаг -lm подключает математическую библиотеку). В Windows аналогично нужно указывать библиотеки через -l.

5. Несовпадение имён или типов (особенно с extern "C")

Если вы смешиваете C и C++ код, линковщик может не найти символ из-за декорирования имён (name mangling). C++ «украшает» имена функций, добавляя информацию о типах аргументов, а C — нет.

// c_lib.h (C-библиотека)
#ifdef __cplusplus
extern "C" {
#endif
void c_function();
#ifdef __cplusplus
}
#endif

// main.cpp
#include "c_lib.h"
int main() {
    c_function();
    return 0;
}

Если забыть extern "C", линковщик будет искать C++ версию имени и не найдёт C-функцию. Исправление: всегда оборачивайте C-заголовки в extern "C".

Как диагностировать ошибку undefined reference: чек-лист

Когда вы видите ошибку линковки, не паникуйте. Пройдите по этому списку:

  • Прочитайте сообщение полностью. Оно всегда говорит, какой символ не найден (например, foo::bar()).
  • Проверьте, есть ли реализация. Напишите grep -rn "foo::bar" *.cpp (или поиск в IDE).
  • Убедитесь, что все .cpp файлы компилируются. В Makefile или IDE проверьте список исходников.
  • Проверьте порядок линковки. В некоторых компиляторах (GCC) порядок библиотек важен: библиотеки должны идти после объектных файлов, которые их используют.
  • Посмотрите на модификаторы доступа. Статические функции или функции в анонимном namespace не видны за пределами файла.

Живой пример: исправляем ошибку шаг за шагом

Представьте, что вы пишете простой калькулятор и получаете ошибку. Вот код:

                

Блоги

Рекомендаций по книгам