Сборка программы
Препроцессинг, компиляция, линковка, видимость, связывание и вычисления на этапе компиляции.
31 вопросов
JuniorТеорияЧастоКак работает #include? Как защититься от двойного включения?
Как работает #include? Как защититься от двойного включения?
#include указывает препроцессору скопировать содержимое файла. От двойного включения защищают классические #ifndef/#define/#endif или #pragma once. Порядок поиска: "" сначала текущий каталог, <> только системные пути.
Типичные ошибки
- ✗Использовать макрос include guard, который не уникален — два заголовка с одинаковым guard молча подавляют один
- ✗Помещать определения (а не только объявления) в заголовки без
inline/static/constexpr— нарушение ODR - ✗Циклические включения — A включает B, B включает A. Используйте forward-декларации для разрыва циклов.
Уточняющие вопросы
- →В чём разница между
#include <file>и#include "file"с точки зрения порядка поиска? - →Как модули C++20 заменяют парадигму include/guard?
JuniorТеорияЧастоОпишите этапы разработки библиотеки или программы.
Опишите этапы разработки библиотеки или программы.
Конвейер: Препроцессинг раскрывает макросы и заголовки, Компиляция преобразует TU в ассемблер, Ассемблирование даёт перемещаемый объект, Компоновка соединяет объекты и разрешает символы. Для библиотеки добавляют упаковку как .a/.so/.dll и потребительскую цель.
Типичные ошибки
- ✗Путать компиляцию со сборкой — компиляция одна TU, сборка — весь конвейер
- ✗Не разделять интерфейс (заголовки) от реализации — раскрывает детали реализации и увеличивает время компиляции
- ✗Забывать, что изменение публичного заголовка разделяемой библиотеки может сломать ABI — используйте PIMPL или версионированные символы
Уточняющие вопросы
- →В чём разница между файлами
.aи.soи когда выбирать каждый? - →Что такое стабильность ABI и почему это важно для эволюции разделяемых библиотек?
JuniorТеорияЧастоКак работают макросы? Подводные камни по сравнению с inline и constexpr.
Как работают макросы? Подводные камни по сравнению с inline и constexpr.
Макросы — текстовые подстановки без типовой безопасности, области видимости и debug-информации; аргументы вычисляются несколько раз, перегрузка невозможна. inline имеет типы и scope; constexpr ещё и выполняется на компиляции. Макросы остаются полезны для include guard и assert.
Типичные ошибки
- ✗Аргумент макроса вычисляется дважды:
MAX(++i, j)увеличиваетiдважды, если он побеждает в сравнении - ✗Макрос без полного заключения в скобки:
#define ADD(a,b) a+bдаёт неверный результат вADD(1,2)*3(1+2*3=7) - ✗Использовать макрос для констант вместо
constexpr— у макросов нет типа, нет адреса, они не видны в отладчиках
Уточняющие вопросы
- →Когда полезен паттерн X-macro и как он работает?
- →Как
if constexpr(C++17) иstatic_assertзаменяют многие применения#if?
JuniorТеорияЧастоЧто такое оптимизация компилятора? Распространённые флаги (-O1/-O2/-O3).
Что такое оптимизация компилятора? Распространённые флаги (-O1/-O2/-O3).
Оптимизация преобразует код ради скорости или меньшего размера без изменения наблюдаемого поведения. Уровни GCC/Clang: -O0 без оптимизации, -O1 базовая, -O2 стандартный production, -O3 агрессивная (может раздуть бинарник), -Os для размера. Production обычно собирают с -O2 или -O3 -DNDEBUG.
Типичные ошибки
- ✗Бенчмаркировать в debug-сборке (
-O0) и делать выводы о производительности — результаты бессмысленны для production - ✗Использовать
-O3без профилирования — может регрессировать производительность из-за давления на кэш инструкций от разросшегося кода - ✗Не сочетать
-O2с-DNDEBUG— release-сборки должны отключать assert
Уточняющие вопросы
- →Что делает
-ffast-mathи почему он может давать неверные результаты для IEEE 754 кода? - →Как Link-Time Optimisation (LTO) расширяет оптимизации на уровне TU?
JuniorТеорияЧастоЧем отличаются препроцессинг, компиляция и линковка?
Чем отличаются препроцессинг, компиляция и линковка?
Препроцессинг раскрывает include, макросы и условные ветки. Компиляция превращает каждую единицу трансляции в объектный файл и проверяет семантику C++. Линковка связывает символы между объектными файлами и библиотеками в исполняемый файл или shared library.
Типичные ошибки
- ✗Ожидать, что компилятор напрямую видит определения из другого .cpp файла
- ✗Класть не-inline определения функций в заголовки и получать duplicate symbols
- ✗Путать ошибку компиляции с unresolved external на этапе линковки
Уточняющие вопросы
- →Что такое единица трансляции?
- →Как посмотреть результат препроцессинга?
JuniorТеорияЧастоКак работает препроцессор? Какие директивы существуют?
Как работает препроцессор? Какие директивы существуют?
Препроцессор — этап текстовой подстановки до компиляции, обрабатывающий строки, начинающиеся с #. Ключевые директивы: #include, #define/#undef, #if/#ifdef/#else/#endif, #pragma, #error, #line. Работает с текстом — без типов и областей видимости.
Типичные ошибки
- ✗Определять макросы без скобок вокруг аргументов:
#define SQ(x) x*xдаёт неверный результат дляSQ(1+2)(1+2*1+2=5) - ✗Не защищать заголовки от двойного включения —
#pragma onceили классические include guard - ✗Злоупотреблять условной компиляцией для скрытия платформо-специфичного кода — предпочитайте runtime полиморфизм или constexpr if
Уточняющие вопросы
- →Что такое
#pragma onceи чем оно отличается от традиционных include guard? - →Что такое вариадический макрос
__VA_ARGS__и когда его использовать?
JuniorДебаггингЧастоКак дебажить ошибку линковки 'undefined reference to'?
Как дебажить ошибку линковки 'undefined reference to'?
Демангл через c++filt, затем проверьте: скомпилировали .cpp с определением, не забыли extern "C", верный ли порядок линковки, подключили ли библиотеку? nm libfoo.a | grep sym покажет, определён ли символ; ldd binary показывает разрешённые shared-зависимости.
Типичные ошибки
- ✗Ставить
-lfooперед объектником, который её использует — порядок важен - ✗Определить функцию в заголовке и использовать из нескольких TU без
inlineили template — multiple definition - ✗Забыть линковать
pthread,m,dl
Уточняющие вопросы
- →Почему порядок линковки важен для статических, но не для shared?
- →Как найти библиотеку, экспортирующую нужный символ?
MiddleДизайнЧастоВ проекте всё ещё используются старые команды CMake уровня директории вроде include_directories и link_libraries, и зависимости постоянно утекают в несвязанные цели. Объясните, что такое target-based (современный) CMake и почему команды target_* со scope PUBLIC/PRIVATE/INTERFACE предпочтительнее старых команд уровня директории.
В проекте всё ещё используются старые команды CMake уровня директории вроде include_directories и link_libraries, и зависимости постоянно утекают в несвязанные цели. Объясните, что такое target-based (современный) CMake и почему команды target_* со scope PUBLIC/PRIVATE/INTERFACE предпочтительнее старых команд уровня директории.
Современный CMake (3.0+) ставит цели в центр: команды target_* со scope PUBLIC/PRIVATE/INTERFACE распространяют свойства потребителям, тогда как старые глобальные команды утекают в несвязанные цели.
Типичные ошибки
- ✗Смешивать глобальный
include_directoriesи per-target — путаница приоритетов - ✗Везде ставить
PUBLIC— лишние зависимости утекают потребителям - ✗Забывать
INTERFACEдля header-only библиотек
Уточняющие вопросы
- →Что значит
INTERFACE-библиотека в CMake? - →Как config-файлы
find_packageиспользуют экспортированные таргеты?
MiddleТеорияЧастоВ чём разница между debug и release сборками?
В чём разница между debug и release сборками?
Debug (-O0 -g) сохраняет полные символы и assert, удобен для пошаговой отладки. Release (-O2 -DNDEBUG) агрессивно оптимизирует и убирает assert'ы. RelWithDebInfo (-O2 -g -DNDEBUG) сохраняет символы для анализа крэшей без потери скорости.
Типичные ошибки
- ✗Бенчмаркировать в debug-сборке — бессмысленно; оптимизация может изменить результаты в 10-100 раз
- ✗Поставлять production без
-DNDEBUG— вызовы assert остаются и могут раскрывать чувствительные данные в сообщениях об ошибках - ✗Не хранить отладочные символы для production бинарников — стектрейсы крэшей без них бесполезны
Уточняющие вопросы
- →Что такое файл
.pdbна Windows и каков его эквивалент на Linux? - →Как работает
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfoпод капотом?
MiddleТеорияЧастоЧто делает extern "C" и зачем он нужен?
Что делает extern "C" и зачем он нужен?
extern "C" отключает искажение имён C++ для включённых объявлений, чтобы символы совпадали с тем, что выдал бы C-компилятор. Нужен при экспорте C++ функций для вызова из C или при импорте функций C-библиотек. Не влияет на члены классов и шаблоны.
Типичные ошибки
- ✗Забывать
extern "C"при реализации C-API колбэка, передаваемого в C-библиотеку — символ искажается, и библиотека не может его найти - ✗Пытаться использовать
extern "C"с классом C++ или шаблоном — поддерживаются только свободные функции и переменные - ✗Не оборачивать блоки
extern "C"в#ifdef __cplusplusв общих заголовках — при включении из C__cplusplusне определён, и блок не нужен; guard избегает синтаксической ошибки
Уточняющие вопросы
- →Как искажение имён кодирует типы параметров функции?
- →Что такое паттерн дизайна C-совместимого API для C++ библиотек (непрозрачный дескриптор)?
MiddleТеорияЧастоЧто такое -fPIC и зачем он нужен для разделяемых библиотек?
Что такое -fPIC и зачем он нужен для разделяемых библиотек?
-fPIC (Position-Independent Code) заставляет компилятор генерировать код без абсолютных адресов — обращения идят через GOT или PC-относительно. Обязателен для разделяемых библиотек, так как те загружаются по любому виртуальному адресу (ASLR).
Типичные ошибки
- ✗Забывать
-fPICдля объектов, входящих в.so— ошибка компоновщика:relocation can not be used when making a shared object - ✗Ненужно использовать
-fPICдля статических библиотек — добавляет небольшие накладные расходы (косвенный доступ через GOT), не нужные при статической компоновке - ✗Путать
-fPIC(большая модель) с-fpic(маленькая модель) — у-fpicменьший лимит размера GOT;-fPICбезопаснее
Уточняющие вопросы
- →Как ASLR взаимодействует с
-fPICдля повышения безопасности? - →В чём разница производительности между PIC и не-PIC кодом и когда это важно?
MiddleТеорияЧастоВ чём разница между scope, linkage и видимостью символов?
В чём разница между scope, linkage и видимостью символов?
Scope определяет, где имя видно в исходном коде. Linkage определяет, относятся ли объявления в разных единицах трансляции к одной сущности. Видимость символов определяет, экспортируется ли символ из shared library или остаётся внутренним для бинарника.
Типичные ошибки
- ✗Думать, что private/protected управляют видимостью для линковщика
- ✗Использовать static для каждого скрытого символа без понимания namespace и anonymous namespace
- ✗Экспортировать слишком много символов из shared library и усложнять поддержку ABI
Уточняющие вопросы
- →Что такое internal linkage?
- →Как default visibility влияет на ABI shared library?
MiddleТеорияЧастоЧто такое Правило Одного Определения (ODR)? Какая ошибка возникает, если два файла определяют одну функцию?
Что такое Правило Одного Определения (ODR)? Какая ошибка возникает, если два файла определяют одну функцию?
ODR: каждая не-inline переменная или функция имеет максимум одно определение во всей программе; inline, шаблоны и constexpr могут определяться в нескольких TU только при идентичности всех определений. Нарушение первого даёт ошибку линковщика; нарушение второго — молчаливое UB.
Типичные ошибки
- ✗Определять не-inline функцию в заголовке, включённом в несколько TU — классическое нарушение ODR, ошибка компоновщика
- ✗Предоставлять разные определения inline функции в разных TU — молча выбирает одно, возможны баги некорректного кода
- ✗Не знать, что
inline-переменные C++17 решают проблему ODR для глобальных констант в заголовках
Уточняющие вопросы
- →Как модули C++20 изменяют правила ODR по сравнению с заголовками?
- →Что такое нарушение ODR, которое компоновщик не может обнаружить, и как санитайзеры помогают?
MiddleТеорияЧастоВ чём разница между статическими и динамическими библиотеками?
В чём разница между статическими и динамическими библиотеками?
Статическая (.a/.lib) архивирует объекты, компонуемые в бинарник на этапе линковки. Динамическая (.so/.dll) загружается в рантайме и разделяется между процессами, давая меньшие бинарники, обновления без перекомпоновки и плагины через dlopen.
Типичные ошибки
- ✗Забывать передавать
-fPICпри компиляции объектов для разделяемой библиотеки — требуется для позиционно-независимого кода - ✗Смешивать C++ объекты от разных компиляторов или настроек в одной разделяемой библиотеке — несовместимость ABI
- ✗Не экспортировать символы явно на Windows — все символы скрыты по умолчанию в
.dll
Уточняющие вопросы
- →Что такое механизм PLT/GOT и почему он добавляет накладные расходы на вызовы динамической библиотеки?
- →Как загрузить разделяемую библиотеку во время выполнения с помощью
dlopen/dlsymна POSIX?
MiddleТеорияЧастоКакие системы автоматизации сборки существуют (Make, CMake, Bazel)?
Какие системы автоматизации сборки существуют (Make, CMake, Bazel)?
Make работает на правилах и выполняет shell-команды из Makefile. CMake — мета-система, генерирующая Make/Ninja/VS из CMakeLists.txt, фактический стандарт в open-source C++. Bazel герметичен и масштабируется для монорепозиториев через строгие BUILD-объявления.
Типичные ошибки
- ✗Использовать
cmake_minimum_requiredс устаревшей версией — может молча применять устаревшее поведение - ✗Использовать glob для сбора файлов (
file(GLOB ...)) — не обновляется автоматически при добавлении файлов - ✗Некорректно смешивать свойства компоновки
INTERFACE,PUBLICиPRIVATE— влияет на распространение транзитивных зависимостей
Уточняющие вопросы
- →В чём разница между
target_include_directoriesPRIVATE и INTERFACE? - →Что делает
cmake --build . --target installи когда его использовать?
MiddleТеорияЧастоКак интегрировать стороннюю библиотеку в C++ проект?
Как интегрировать стороннюю библиотеку в C++ проект?
Варианты: системный пакет через find_package — проще всего, но без пиннинга; CMake FetchContent для клонирования и сборки из исходников; git submodule для контроля в дереве; Conan/vcpkg для кэша бинарников и версионирования; ручное копирование для header-only библиотек.
Типичные ошибки
- ✗Фиксировать версию библиотеки через
FetchContent_Declareбез блокировки хэша — обновления могут молча сломать сборку - ✗Компоновать с неверной конфигурацией (Debug vs Release) предсобранной библиотеки на Windows
- ✗Не читать гарантии стабильности ABI библиотеки перед обновлением минорной версии
Уточняющие вопросы
- →В чём разница между режимами CONFIG и MODULE для
find_packageв CMake? - →Как vcpkg интегрируется с CMake через файл toolchain?
SeniorТеорияЧастоКак работать с CMake? Цели, зависимости, правила установки.
Как работать с CMake? Цели, зависимости, правила установки.
Современный CMake (3.x) основан на целях: add_library/add_executable создают цели, а команды target_* со скоупами PRIVATE/PUBLIC/INTERFACE распространяют свойства потребителям. install(TARGETS ... EXPORT ...) создаёт перемещаемый пакет, используемый через find_package(Foo CONFIG).
Типичные ошибки
- ✗Использовать
include_directories/link_libraries(глобальные) вместоtarget_*вариантов — загрязняет все цели - ✗Не задавать
CMAKE_INSTALL_PREFIX— по умолчанию устанавливает в/usr/local, потенциально загрязняя систему - ✗Забывать
GNUInstallDirsдля переносимых путей установки — хардкодингlibилиincludeломает multilib-системы
Уточняющие вопросы
- →В чём разница между
cmake -G "Ninja"иcmake -G "Unix Makefiles"? - →Как написать
FooConfig.cmake, который downstream-проект может использовать черезfind_package?
MiddleПроизводительностьИногдаЧто делает ccache и как он работает в CI?
Что делает ccache и как он работает в CI?
ccache — кеш компилятора: хеширует препроцессированный исходник плюс флаги и хранит получившийся объектник, поэтому одинаковые перекомпиляции минуют компилятор. Используйте CC='ccache gcc' и сохраняйте директорию кеша между прогонами CI.
Типичные ошибки
- ✗Забыть
CCACHE_BASEDIRв CI — разные рабочие директории инвалидируют кеш - ✗Не сохранять директорию кеша между прогонами CI — каждая сборка с нуля
- ✗Смешивать ccache и sccache без понимания различий
Уточняющие вопросы
- →Чем отличается sccache (Mozilla, поддерживает удалённый кеш)?
- →Как дебажить устаревшее попадание ccache?
MiddleТеорияИногдаКак экспортировать/импортировать функции из динамической библиотеки?
Как экспортировать/импортировать функции из динамической библиотеки?
На Windows экспорт помечают __declspec(dllexport), импорт — __declspec(dllimport). На Linux/macOS используют -fvisibility=hidden плюс __attribute__((visibility("default"))) для явного публичного API. Переносимая идиома — макрос, раскрывающийся по ОС и по направлению.
Типичные ошибки
- ✗Не использовать
dllimportу потребителя — компилятор генерирует косвенный вызов через заглушку вместо прямого PLT вызова - ✗Экспортировать члены класса C++ через границы DLL без слоя совместимости ABI — ненадёжно между версиями компиляторов
- ✗Забывать, что на Linux
-fvisibility=hiddenнужно задавать при компиляции, а не только при компоновке
Уточняющие вопросы
- →Как создать import library (
.lib) из.dllна Windows? - →Что такое версионирование символов через
__attribute__((symver))на Linux?
MiddleТеорияИногдаЧто такое DLL hell? Как избежать?
Что такое DLL hell? Как избежать?
DLL hell — когда несколько приложений требуют разных несовместимых версий одной разделяемой библиотеки, что вызывает крэши или ошибки undefined symbol. Меры: версионирование символов (SONAME), SxS-сборки на Windows, статическая компоновка, контейнеры и строгие правила ABI.
Типичные ошибки
- ✗Изменять порядок или тип публичных членов структуры/класса и перекомпилировать только библиотеку — нарушение ABI, крэш во время выполнения
- ✗Экспортировать не-POD C++ объекты через границы разделяемых библиотек — искажение имён и различия ABI делают это ненадёжным
- ✗Не увеличивать SONAME при ABI-несовместимых изменениях — компоновщик и ОС загружают неверную версию
Уточняющие вопросы
- →Как
rpathпомогает контролировать, какая разделяемая библиотека загружается во время выполнения? - →Что такое
lddи как его использовать для диагностики отсутствующих зависимостей разделяемых библиотек?
MiddleПроизводительностьИногдаЧто такое link-time optimization (LTO) и что она даёт?
Что такое link-time optimization (LTO) и что она даёт?
LTO откладывает оптимизации до этапа линковки, где видна вся программа. Компилятор выкладывает в объектники IR; линкер запускает оптимизацию через TU, давая кросс-TU инлайнинг и devirtualisation. Включается через -flto (gcc/clang) или /GL+/LTCG (MSVC).
Типичные ошибки
- ✗Смешивать LTO и не-LTO объектники из разных версий компилятора — ошибки линковки или miscompile
- ✗Включать LTO без измерений — overhead съедает выигрыш на малых проектах
- ✗Забыть
-fltoна этапе линковки в дополнение к компиляции
Уточняющие вопросы
- →Что такое ThinLTO и чем отличается от full LTO?
- →Как
-fwhole-programвзаимодействует с LTO?
MiddleТеорияИногдаКак работают C++ package managers вроде Conan и vcpkg?
Как работают C++ package managers вроде Conan и vcpkg?
Оба скачивают и собирают (или подтягивают готовые) библиотеки по versioned-рецептам. Conan — Python-рецепты с profiles и lockfile. vcpkg — CMake-portfiles и манифесты vcpkg.json. Оба разрешают транзитивные зависимости.
Типичные ошибки
- ✗Смешивать system, vcpkg и Conan версии одной библиотеки — link-конфликты
- ✗Не пинить версии — сборка ломается при обновлении зависимости
- ✗Использовать package manager для header-only, где проще git submodule
Уточняющие вопросы
- →Что такое Conan profile и зачем разные для каждого компилятора?
- →Чем vcpkg manifest mode отличается от classic?
MiddleПроизводительностьИногдаЧто такое precompiled header (PCH) и когда он ускоряет сборку?
Что такое precompiled header (PCH) и когда он ускоряет сборку?
PCH — это разобранный снимок часто включаемого набора заголовков, сохранённый на диск, чтобы последующие компиляции пропускали разбор. CMake включает его через target_precompile_headers. PCH специфичен для компилятора/версии/флагов; современная замена — модули C++20.
Типичные ошибки
- ✗Класть в PCH часто меняющиеся заголовки — каждое изменение пересобирает всё
- ✗Смешивать разные флаги компиляции при генерации и использовании PCH
- ✗Добавлять PCH в маленький проект — сложность без выигрыша
Уточняющие вопросы
- →Как C++20 модули заменяют PCH?
- →Что такое
ccacheи чем отличается от PCH?
SeniorПроизводительностьИногдаПроект компилируется медленно. Как ускорить компиляцию?
Проект компилируется медленно. Как ускорить компиляцию?
Сначала профилируйте, затем применяйте: ccache для переиспользования объектников, Ninja для параллелизма, PCH для тяжёлых стабильных заголовков, forward declarations для прореживания include, unity-сборки или модули C++20 против избыточного разбора и distcc для масштабирования по машинам.
Типичные ошибки
- ✗Добавлять PCH ко всем целям — инвалидация PCH вызывает полную перекомпиляцию; пользу приносят только стабильные, широко включаемые заголовки
- ✗Использовать unity builds в разработке — скрывает проблемы зависимостей заголовков и делает инкрементальные сборки бесполезными
- ✗Не профилировать сборку через
ninja -t graphили аналог для поиска реального узкого места
Уточняющие вопросы
- →Как
-ftime-traceClang определяет, какие заголовки занимают больше всего времени компиляции? - →Что такое инструмент IWYU (Include What You Use) и как он помогает?
SeniorТеорияИногдаКаковы сложности кроссплатформенного C++ кода?
Каковы сложности кроссплатформенного C++ кода?
Сложности кроссплатформенности: API ОС и пути, различия компиляторов и ABI, размеры типов (<cstdint>), порядок байт, ABI разделяемых библиотек и системы сборки (CMake).
Типичные ошибки
- ✗Жёстко кодировать разделители пути — используйте
std::filesystem::path, который прозрачно обрабатывает/и\ - ✗Использовать
longдля 64-битных значений —long— 32-битный на Windows даже в 64-битном режиме; используйтеint64_t - ✗Считать, что
charзнаковый — это зависит от реализации; используйте явныйsigned char/unsigned charилиstd::byteдля байтовых операций
Уточняющие вопросы
- →Как управлять платформо-специфичным кодом без моря
#ifdef? - →Что является Windows-специфичным эквивалентом MSVC
/W4для GCC-Wall -Wextra?
SeniorДизайнИногдаВы настраиваете сборку для большого многомодульного C++-проекта, над которым несколько команд будут работать годами. Опишите, как вы спроектировали бы и структурировали его систему сборки. Затроньте: воспроизводимость на разных машинах и в CI, сохранение быстрых пересборок по мере роста кодовой базы, чистое выражение зависимостей между модулями и то, как организовать исходники и цели. Сформулируйте свойства, которые ваш дизайн обязан гарантировать, не привязываясь к одному конкретному инструменту.
Вы настраиваете сборку для большого многомодульного C++-проекта, над которым несколько команд будут работать годами. Опишите, как вы спроектировали бы и структурировали его систему сборки. Затроньте: воспроизводимость на разных машинах и в CI, сохранение быстрых пересборок по мере роста кодовой базы, чистое выражение зависимостей между модулями и то, как организовать исходники и цели. Сформулируйте свойства, которые ваш дизайн обязан гарантировать, не привязываясь к одному конкретному инструменту.
Стремитесь к герметичным сборкам (фиксированные инструменты, воспроизводимость), инкрементальным через точный граф зависимостей, параллельному выполнению и одной CMake-цели на модуль с PRIVATE/INTERFACE зависимостями. Сборка вне исходников, исходники — явно.
Типичные ошибки
- ✗Помещать все исходные файлы под единственный верхнеуровневый CMakeLists.txt — становится необслуживаемым; используйте
add_subdirectory - ✗Не использовать
target_link_librariesтранзитивно — транзитивно распространяет заголовки и флаги - ✗Не кэшировать вывод компилятора (ccache/sccache) в CI — каждая чистая сборка перекомпилирует всё с нуля
Уточняющие вопросы
- →Что такое предварительно скомпилированные заголовки (PCH) и когда они значительно ускоряют сборку?
- →Как Unity build (amalgamation) обменивает скорость компиляции на гранулярность объектных файлов?
SeniorТеорияРедкоЧто такое формат ELF и какие секции типично содержит исполняемый файл?
Что такое формат ELF и какие секции типично содержит исполняемый файл?
ELF — бинарный формат на Linux/BSD. Header: архитектура, точка входа, таблицы сегментов/секций. Секции: .text (код), .rodata (строковые литералы), .data (init globals), .bss (zero-init, не занимает места в файле), .symtab/.strtab (символы), .dynsym/.dynstr (dynamic), .plt/.got (lazy резолвинг), .debug_* (DWARF).
Типичные ошибки
- ✗Путать sections (link view) и segments (load view)
- ✗Класть большие инициализированные массивы в
.data, когда хватит zero-init в.bss - ✗Забывать, что
.bssне занимает место в файле, но занимает память в рантайме
Уточняющие вопросы
- →Как dynamic linker резолвит символы (PLT/GOT lazy binding)?
- →Чем PIE, PIC и обычный executable отличаются?
SeniorПроизводительностьРедкоЧто такое profile-guided optimization (PGO) и когда оно оправдывает сложность сборки?
Что такое profile-guided optimization (PGO) и когда оно оправдывает сложность сборки?
PGO — двухстадийная сборка: компилируется с -fprofile-generate, прогоняется представительная нагрузка для сбора счётчиков, затем перекомпилируется с -fprofile-use, и компилятор раскладывает горячие пути и инлайнит по реальным данным. Выигрыш 5-20% для perf-critical бинарей.
Типичные ошибки
- ✗Профилировать на нерепрезентативной нагрузке — пессимизация на реальном трафике
- ✗Забыть очистить profile-данные при существенных изменениях кода
- ✗Делать PGO без LTO — оба вместе дают лучший результат
Уточняющие вопросы
- →Что такое AutoFDO и чем отличается от instrumentation PGO?
- →Как PGO работает с template-heavy кодом?
SeniorТеорияРедкоЧем отличаются RPATH, RUNPATH и LD_LIBRARY_PATH?
Чем отличаются RPATH, RUNPATH и LD_LIBRARY_PATH?
Все три управляют поиском библиотек dynamic linker'ом. RPATH (DT_RPATH) встроен и проверяется до LD_LIBRARY_PATH. RUNPATH (DT_RUNPATH) тоже встроен, но проверяется после него. LD_LIBRARY_PATH — env-переменная с промежуточным приоритетом.
Типичные ошибки
- ✗Хардкодить абсолютный RPATH — ломает перенос установки
- ✗Ставить RPATH на setuid-бинарь — отключается по безопасности
- ✗Путать приоритет RPATH и RUNPATH — меняет поведение с LD_LIBRARY_PATH
Уточняющие вопросы
- →Как работает
$ORIGINв RPATH? - →Что делает
patchelf?
SeniorТеорияРедкоЧто такое статические и динамические анализаторы кода? Примеры.
Что такое статические и динамические анализаторы кода? Примеры.
Статический анализ исследует код без запуска (clang-tidy, cppcheck, предупреждения компилятора, PVS-Studio). Динамический анализ инструментирует работающую программу (AddressSanitizer, UBSan, ThreadSanitizer, Valgrind). Большинство проектов используют оба.
Типичные ошибки
- ✗Относиться к предупреждениям статического анализа как к необязательным — ложные срабатывания редки в современных инструментах; трактуйте их как ошибки в CI
- ✗Запускать санитайзеры только на малых юнит-тестах — многие баги проявляются только при реалистичной многопоточной нагрузке
- ✗Совмещать ASan и TSan — они не могут работать одновременно; раздельные запуски или отдельные CI задачи
Уточняющие вопросы
- →Как
clang-tidyинтегрируется с CMake черезCMAKE_CXX_CLANG_TIDY? - →В чём разница между AddressSanitizer и Valgrind memcheck с точки зрения накладных расходов?
SeniorТеорияРедкоЗачем shared-библиотеке скрывать внутренние символы по умолчанию и как это сделать?
Зачем shared-библиотеке скрывать внутренние символы по умолчанию и как это сделать?
GCC/Clang по умолчанию экспортируют все external-символы, раздувая таблицу символов и грозя конфликтами. Компилируйте с -fvisibility=hidden и помечайте только публичные __attribute__((visibility("default"))). Результат: быстрее dlopen, меньше бинарник, лучше оптимизация.
Типичные ошибки
- ✗Помечать класс целиком default visibility вместо конкретных публичных методов
- ✗Забывать, что инстанциации шаблонов на Windows требуют явной visibility (другой механизм)
- ✗Использовать
stripвместо настройки visibility — теряется debug info
Уточняющие вопросы
- →Как visibility связана с
dllexport/dllimportна Windows? - →Что такое version-script и когда он нужен?