Компиляция и линковка в 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++ или Clang (Linux/macOS/MinGW):
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
- MSVC (Windows):
cl /std:c++17 /EHsc /c sum.cpp /Fobuild\sum.obj
cl /std:c++17 /EHsc /c main.cpp /Fobuild\main.obj
Шаг 2. Линковка: собираем исполняемый файл
Линкер склеивает объектные файлы и внешние библиотеки в исполняемый файл.
- g++/Clang:
g++ build/main.o build/sum.o -o app
- MSVC:
link build\main.obj build\sum.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 /Fobuild\sum.obj
lib /OUT:build\mymath.lib build\sum.obj
cl main.cpp build\mymath.lib /Fe:app.exe
Динамические библиотеки (.so, .dll, .dylib)
Динамическая библиотека подключается во время выполнения. Итоговый бинарник меньше, а библиотеку можно обновлять отдельно.
- Linux/macOS (GCC/Clang):
// создаём общую библиотеку
g++ -fPIC -shared sum.cpp -o build/libmymath.so
// линкуем приложение и настраиваем поиск .so рядом с бинарником
g++ main.cpp -Lbuild -lmymath -Wl,-rpath,'$ORIGIN' -o app
- Windows (MSVC): экспорт/импорт символов через макросы:
// 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.
Порядок линковки и поиск библиотек
- Для GCC/Clang порядок важен: сначала объектные файлы, затем библиотеки:
g++ main.o -L. -lmymath. - Поиск:
-L<путь>добавляет каталог,-l<имя>ищет lib<имя>.so/.a. На Windows с MSVC указывают .lib явно. - В runtime: Linux — LD_LIBRARY_PATH или rpath; Windows — рядом с exe или в PATH; macOS — @rpath/@loader_path.
Типичные ошибки компиляции и линковки
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++ стандарт).
Практические советы
- Структурируйте проект: include/ для заголовков, src/ для .cpp, build/ для артефактов.
- Флаги по умолчанию:
-Wall -Wextra -Wpedantic(GCC/Clang), в Debug ещё-g; в Release —-O2 -DNDEBUG. Для MSVC:/W4 /Ziв Debug,/O2 /DNDEBUGв Release. - Следите за порядком библиотек при линковке в GCC/Clang.
- Динамические библиотеки делайте с чётким API и версионированием; статические — для простого деплоя без зависимостей на целевой машине.
- Освойте CMake, когда поймёте основы компиляции и линковки C++ — это облегчит кроссплатформенную сборку.
Итоги
Теперь вы понимаете, как устроены компиляция и линковка C++: объектные файлы, статические и динамические библиотеки, команды для g++/Clang/MSVC, а также типичные ошибки и пути их решения. Следующим шагом может стать освоение систем сборки и углубление в оптимизацию и отладку.
Хотите быстрее перейти от теории к практике на реальных задачах? Рекомендую изучить программу курса «Программирование на C++ с Нуля до Гуру» — он системно закрывает пробелы и даёт много практики: Прокачать C++ и сборку проектов — посмотреть программу курса.
-
Создано 17.04.2026 17:01:01
-
Михаил Русаков

Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.