Компиляция и линковка в C++ для начинающих: от .cpp до .exe с g++, Clang и MSVC
Эта статья — практическое руководство по теме «компиляция и линковка C++». Разберём, что такое объектные файлы, как собрать проект из нескольких .cpp, чем отличаются статические и динамические библиотеки, а также как запускать команды для g++, Clang и MSVC. В конце — типичные ошибки и быстрые способы их починить.
Минимальный пример: два .cpp и заголовок
Создадим крошечную библиотеку суммирования и использующий её main:
// sum.h
#pragma once
int sum(int a, int b);
// sum.cpp
#include "sum.h"
int sum(int a, int b) { return a + b; }
// main.cpp
#include <iostream>
#include "sum.h"
int main() {
std::cout << sum(2, 3) << "n";
return 0;
}
Шаг 1. Компиляция в объектные файлы (.o, .obj)
Компилятор переводит каждый .cpp в объектный файл с машинным кодом, но ещё без «склейки» между файлами.
g++ -std=c++17 -Wall -Wextra -O2 -c sum.cpp -o build/sum.o
g++ -std=c++17 -Wall -Wextra -O2 -c main.cpp -o build/main.o
cl /std:c++17 /EHsc /c sum.cpp /Fobuildsum.obj
cl /std:c++17 /EHsc /c main.cpp /Fobuildmain.obj
Шаг 2. Линковка: собираем исполняемый файл
Линкер склеивает объектные файлы и внешние библиотеки в исполняемый файл.
g++ build/main.o build/sum.o -o app
link buildmain.obj buildsum.obj /OUT:app.exe
// либо одной командой cl:
cl /std:c++17 /EHsc main.cpp sum.cpp /Fe:app.exe
Статические библиотеки (.a, .lib)
Статическая библиотека упаковывает объектные файлы. При линковке код копируется в итоговый бинарник (удобно для простого деплоя).
- Собираем объектные файлы:
g++ -c sum.cpp -o build/sum.o
- Создаём статическую библиотеку:
ar rcs build/libmymath.a build/sum.o
- Линкуем с ней приложение:
g++ -c main.cpp -o build/main.o
g++ build/main.o -Lbuild -lmymath -o app
На Windows (MSVC) статическая библиотека — .lib:
cl /c sum.cpp /Fobuildsum.obj
lib /OUT:buildmymath.lib buildsum.obj
cl main.cpp buildmymath.lib /Fe:app.exe
Динамические библиотеки (.so, .dll, .dylib)
Динамическая библиотека подключается во время выполнения. Итоговый бинарник меньше, а библиотеку можно обновлять отдельно.
// создаём общую библиотеку
g++ -fPIC -shared sum.cpp -o build/libmymath.so
// линкуем приложение и настраиваем поиск .so рядом с бинарником
g++ main.cpp -Lbuild -lmymath -Wl,-rpath,'$ORIGIN' -o app
// mymath.h
#pragma once
#ifdef _WIN32
#ifdef MYMATH_EXPORTS
#define MYMATH_API __declspec(dllexport)
#else
#define MYMATH_API __declspec(dllimport)
#endif
#else
#define MYMATH_API
#endif
MYMATH_API int sum(int a, int b);
// mymath.cpp
#include "mymath.h"
int sum(int a, int b) { return a + b; }
// сборка DLL и импортной библиотеки
cl /std:c++17 /EHsc /LD mymath.cpp /FemyMath.dll /DMYMATH_EXPORTS
// линковка приложения с импортной библиотекой
cl /std:c++17 /EHsc main.cpp myMath.lib /Fe:app.exe
Для запуска положите myMath.dll рядом с app.exe или пропишите путь в PATH.
Порядок линковки и поиск библиотек
g++ main.o -L. -lmymath.-L<путь> добавляет каталог, -l<имя> ищет lib<имя>.so/.a. На Windows с MSVC указывают .lib явно.Типичные ошибки компиляции и линковки
1) undefined reference (GCC/Clang) / unresolved external symbol (MSVC)
Причины: забыли добавить .o/.lib/.a к линковке; несовпадение сигнатур; отключили нужную библиотеку; нарушен extern «C» при линковке с C-библиотекой.
Как чинить: убедитесь, что все определения присутствуют при линковке; проверяйте порядок для GCC; сигнатуры функций в .h и .cpp совпадают.
2) multiple definition
Причины: определили функцию в заголовке без inline; один и тот же объектный файл подключён дважды; дублирующие реализации в разных файлах.
Как чинить: переносите реализацию в .cpp и оставляйте в .h только объявления; для маленьких функций в .h используйте inline или static (понимая последствия).
3) DLL не находится в runtime
Причины: Windows — .dll не рядом с exe и не в PATH; Linux — не настроен rpath или LD_LIBRARY_PATH.
Как чинить: кладите .dll рядом с .exe; на Linux используйте -Wl,-rpath,'$ORIGIN' или переменную окружения.
4) Несовместимость ABI
Причины: линковка библиотек, собранных другим компилятором/стандартной библиотекой C++ или с иными флагами.
Как чинить: пересобирайте зависимости одним и тем же компилятором и профилем (Debug/Release, C++ стандарт).
Практические советы
-Wall -Wextra -Wpedantic (GCC/Clang), в Debug ещё -g; в Release — -O2 -DNDEBUG. Для MSVC: /W4 /Zi в Debug, /O2 /DNDEBUG в Release.Итоги
Теперь вы понимаете, как устроены компиляция и линковка C++: объектные файлы, статические и динамические библиотеки, команды для g++/Clang/MSVC, а также типичные ошибки и пути их решения. Следующим шагом может стать освоение систем сборки и углубление в оптимизацию и отладку.
Хотите быстрее перейти от теории к практике на реальных задачах? Рекомендую изучить программу курса «Программирование на C++ с Нуля до Гуру» — он системно закрывает пробелы и даёт много практики: Прокачать C++ и сборку проектов — посмотреть программу курса.




