c-cpp-cookies
  • Полезные материалы
    • About
  • Настройка окружения
    • Git
      • Git CLI
      • Git GUI
      • Git Web
      • Git в среде разработке
        • Visual Studio
        • Visual Studio Code
        • CLion
        • Qt Creator
    • Стандарты (они же ISO)
    • Qt about
    • IDE
      • Visual Studio Code
        • Код в Visual Studio Code
        • Разработка в Visual Studio Code
      • Visual Studio
        • Настройки Visual Studio и проектов
        • Код в Visual Studio
        • Visual Studio + GitHub
      • Qt + Qt Creator
        • Установка Qt и QtCreator
        • Примеры проектов на Qt
      • CLion
        • Настройки CLion
        • Код в CLion
        • CLion + GitHub
    • Компиляторы
      • Clang + Visual Studio
      • Clang + CLion
  • Best practices
    • Антипаттерны и способы улучшения кода
      • 0. Освобождение ресурсов
      • 1. Чтение данных из файла
      • 2. Открытие файлов
      • 3. Объявление переменных
      • 4. Выделение памяти
      • 5. Необдуманный код
      • 6. Глубокие if
      • 7. Длинные if
      • 9. Non-void функции
      • 10. Создание массивов
      • 11. Проверка формата файла
  • Сборка программы
    • Сборка программы
    • Системы сборки / Build systems
      • Проект и решение
      • Make
      • Ninja
      • CMake
      • MSBuild
    • Подключение внешних библиотек
    • Библиотеки
  • Отладка
    • Отладка / Debugging
    • Запуск программы
    • Точки останова / Breakpoints
      • Обычные точки останова / General breakpoint
      • Условные точки останова / Conditional breakpoint
      • Зависимые точки останова / Dependent breakpoint
      • Временные точки останова / Temporary breakpoint
      • Точки трассировки / Breakpoint actions и tracepoints
    • Стек вызовов / Call stack
    • Локальные переменные / Locals
    • Просмотр области памяти / Memory view
    • Исключения / Exceptions
  • Анализ
    • Поиск утечек памяти
    • Санитайзеры
  • Возможности IDE
    • Возможности IDE
    • Продвинутый текстовый редактор
    • Закладки / Bookmarks
    • Платформы / Platforms
    • Конфигурации сборки / Build configurations
    • Шаблонные проекты / Project templates
    • Терминал / Terminal
    • Сборка / Build
    • Запуск программы / Run program
    • Code style
    • Работа с Github Actions
  • Тестирование
    • Тестирование
    • Google test
      • Gtest в VS
      • Gtest в CLion
      • Gtest в VSCode
    • Покрытие кода (code coverage)
Powered by GitBook
On this page
  • Статическая библиотека
  • Сборка статической библиотеки
  • Сборка со статической библиотекой
  • Динамическая библиотека
  • Импорт и экспорт
  • Сборка динамической библиотеки
  • Подключение динамических библиотек
  • Различия между exe и dll
  1. Сборка программы

Библиотеки

PreviousПодключение внешних библиотекNextОтладка / Debugging

Last updated 1 month ago

Термин «библиотека» может иметь несколько значений в зависимости от контекста

  1. Библиотека как файл (library) – файл с скомпилированным кодом (функций, классов, переменных и других сущностей). Библиотеки разделяются на динамические и статические.

  2. Библиотека как набор готового кода – набор файлов библиотек и вспомогательных файлов (заголовочных, ресурсов и пр.). Иногда выступает синонимом "фреймворку" (framework).

Упрощение разработки: не нужно писать код заново, можно использовать готовые решения.

Сокращение времени сборки проекта: код библиотеки уже скомпилирован.

Далее пойдёт описание библиотек как файла. Пример библиотеки как набора готового кода можно посмотреть в разделе .

Статическая библиотека

Статическая библиотека – объектный файл, код из которого выборочно или полностью вставляется в программу на этапе линковки.

Код, подключенный из статической библиотеки, становится частью программы – для использования кода не нужно устанавливать еще какой-либо файл. Линковщик выполняет соединение объектных файлов библиотек и объектных файлов вашей программы во время создания исполняемого файла.

Расширения объектных файлов статических библиотек в разных ОС.

Расширение
ОС

.a

UNIX

.lib

Windows

все необходимые функции включаются в один исполняемый файл.

исполняемый файл занимает больше места на диске и в памяти, если присоединяет код из объектных библиотечных файлов;

при обнаружении ошибок в библиотеке требуется пересборка всех программ, использующих библиотеку.

Сборка статической библиотеки

Developer command prompt/Developer PowerShell x64
clang -c -o lib_source.obj lib_source.c
lib /subsystem:console /machine:x64 /out:my_static_lib.lib lib_source.obj
Developer command prompt/Developer PowerShell x86
clang -c -o lib_source.obj lib_source.c -m32
lib /subsystem:console /machine:x86 /out:my_static_lib.lib lib_source.obj
PowerShell x64
& "C:\Program Files\Microsoft Visual Studio 2022\Common7\Tools\Launch-VsDevShell.ps1" -Arch amd64 -HostArch amd64
clang -c -o lib_source.obj lib_source.c
lib /subsystem:console /machine:x64 /out:my_static_lib.lib lib_source.obj
PowerShell x86
& "C:\Program Files\Microsoft Visual Studio 2022\Common7\Tools\Launch-VsDevShell.ps1" -Arch x86 -HostArch x86
clang -c -o lib_source.obj lib_source.c -m32
lib /subsystem:console /machine:x86 /out:my_static_lib.lib lib_source.obj
cmd x64
"C:\Program Files\Microsoft Visual Studio 2022\Common7\Tools\VsDevCmd.bat" -startdir=auto -arch=x64 -host_arch=x64
clang -c -o lib_source.obj lib_source.c
lib /subsystem:console /machine:x64 /out:my_static_lib.lib lib_source.obj
cmd x86
"C:\Program Files\Microsoft Visual Studio 2022\Common7\Tools\VsDevCmd.bat" -startdir=auto -arch=x86 -host_arch=x86
clang -c -o lib_source.obj lib_source.c -m32
lib /subsystem:console /machine:x86 /out:my_static_lib.lib lib_source.obj
shell
clang -c -o lib_source.o lib_source.c -fPIC
ar r library.a lib_source.o

-fPIC – Position-independent code

Сборка со статической библиотекой

Сборка с библиотекой (общепринятый вариант ключей компиляции)
clang -o my.exe my_source.c -L"path/to/dir_with_lib_files" -lmy_static_lib
Сборка с библиотекой (более новый вариант)
clang my_source.c -L "path/to/dir_with_lib_files" -l my_static_lib.lib

Динамическая библиотека

Динамические библиотеки не является отдельными исполняемыми файлами, а выполняются в контексте вызывающих их программ. ОС загружает динамическую библиотеку в область памяти программы либо при загрузке приложения (неявная компоновка), либо по запросу во время выполнения (явная компоновка).

Подробнее

Динамическая компоновка – возможность операционной системы, позволяющей исполняемому файлу вызывать функции или использовать ресурсы, хранящиеся в отдельном файле. Эти функции и ресурсы можно компилировать и развертывать отдельно от использующих их исполняемых файлов.

Динамические библиотеки также упрощают совместное использование функций и ресурсов различными исполняемыми файлами. Несколько приложений могут осуществлять одновременный доступ к содержимому одной копии динамической библиотеки в памяти.

Расширения имен файлов динамических библиотек в разных ОС.

Расширение
Расшифровка
ОС

.so

shared object

Unix

.dll

dynamic link library

Windows

.dylib

dynamic library

MacOS

Импорт и экспорт

Импортировать открытые символы в приложение или экспортировать функции из динамической библиотеки можно следующим образом:

Требуется явно специфицировать, что функция/структура будут экспортированы:

__declspec(dllexport) int my_func(int a) { ... }

Импорт открытых символов и объектов библиотеки:

__declspec(dllimport) int my_func(int a);

Все символы по умолчанию видны пользователю разделяемой библиотеки. То есть все объекты и функции экпортируются (если не static).

Можно изменить это поведение, задав ключ компиляции и аттрибуты функциям:

clang -fvisibility=hidden ...
__attribute__((visibility("default"))) int my_func(int a) { ... }

Сборка динамической библиотеки

clang -shared my_lib_src.c -o my_lib.dll

[Windows stdout]

Creating library my_lib.lib and object my_lib.exp

Подключение динамических библиотек

Прежде чем вызывать подпрограммы, включённые в динамическую библиотеку, их надо импортировать в программу. Возможны следующие варианты:

  1. Статическая линковка.

  2. Загрузка библиотеки при помощи системных вызовов.

Статическая линковка динамической библиотеки

После загрузки программы ОС начинает поиск dll:

  • директория, из которой загружено приложение, требующее dll-библиотеку;

  • текущая директория;

  • системные пути;

  • пути из переменной PATH.

После загрузки запускается сам исполняемый файл.

Загрузка динамической библиотеки в процессе выполнения программы

При динамической компоновке включаются только те сведения, которые позволяют ОС найти и загрузить динамическую библиотеку, содержащую элемент данных или функцию, во время выполнения.

Доступ к подпрограммам динамической библиотеки можно получить при помощи системных вызовов (например, WinAPI: LoadLibrary, FreeLibrary и GetProcAddress).

Процесс загрузки DLL

При сборке исполняемого файла, который вызывает динамическую библиотеку, компоновщик использует экспортированные символы в библиотеке импорта, чтобы сохранить эти сведения для загрузчика Windows. Когда загрузчик загружает динамическую библиотеку, она сопоставляется с областью памяти приложения. Для выполнения операций инициализации, необходимых динамической библиотеке, вызывается специальная функция DllMain из динамической библиотеки (если она имеется).

Рассмотрим следующий пример:

  • mydll.c – исходник динамической библиотеки, содержащий 2 функции – одна экспортируется, другая является внутренней для этой библиотеки;

  • main.c – исходник программы-таргета, в которой будут использоваться функции из библиотеки;

  • mydll.dll – файл динамической библиотеки, собранной из mydll.c.

mydll.c
unsigned powi(int x, unsigned y)
{
  int z = 1;
  for (unsigned i = 0; i < y; i++)
    z *= x;
  return z;
}

__declspec(dllexport) unsigned powu(unsigned x, unsigned y)
{
  unsigned z = 1;
  for (unsigned i = 0; i < y; i++)
    z *= x;
  return z;
}
main.c
#include <Windows.h>
#include <stdio.h>

int main(void)
{
  HMODULE hdll = LoadLibraryA("mydll.dll");
  if (!hdll)
  {
    fprintf(stderr, "!load mydll.dll");
    return -1;
  }
  unsigned (*powu)(unsigned x, unsigned y);
  powu = (void*)GetProcAddress(hdll, "powu");
  if (!powu)
  {
    fprintf(stderr, "!load powu");
    FreeLibrary(hdll);
    return -1;
  }

  unsigned res = powu(2, 5);
  FreeLibrary(hdll);
  return res;
}

Различия между exe и dll

Хотя и динамические библиотеки, и приложения являются исполняемыми модулями, они отличаются некоторыми особенностями. Наиболее очевидное – динамическую библиотеку нельзя запустить.

С точки зрения системы, между приложениями и динамическими библиотеками имеется два существенных различия:

  1. В системе может одновременно выполняться несколько экземпляров приложения. Экземпляр динамической библиотеки может быть только один.

  2. Приложение может загружаться как процесс. Ему могут принадлежать такие компоненты, как стек, потоки выполнения, глобальная память, дескрипторы файлов и очередь сообщений. У динамической библиотеки таких компонентов быть не может.

[Windows] После успешной компиляции вы получите файлы .dll и .lib. Файл с расширением lib – библиотека импорта, которая требуется при статическом подключении динамической библиотеки.

экономия памяти и сокращение времени на подкачку за счёт использования одной библиотеки несколькими процессами;

возможность исправления ошибок (достаточно заменить файл библиотеки и перезапустить работающие программы) без изменения кода основной программы;

с помощью явной компоновки можно обнаруживать и загружать динамические библиотеки во время выполнения. Например, это могут быть расширения приложения, которые добавляют новые функциональные возможности без повторной сборки и развертывания приложения;

динамическая компоновка упрощает поддержку приложений, написанных на разных языках программирования, вызывающих одну и ту же функцию в динамической библиотеке при условии соблюдения соглашения о ее вызове.

возможность нарушения API – при внесении изменений в библиотеку существующие программы могут перестать работать, став несовместимыми по интерфейсу;

конфликт версий динамических библиотек – разные программы могут нуждаться в разных версиях библиотеки;

доступность одинаковых функций по одинаковым адресам в разных процессах – упрощает эксплуатацию уязвимостей.

динамическая библиотека – системозависима.

[Windows] DEF-файлы можно использовать для или .

Без найденных на старте dll и нужных импортов программа в принципе не запуститься.

Нужна библиотека импорта на этапе линковки.

позволяет уменьшать требуемое количество памяти;

появляется возможность запускать приложение, даже если какие-то библиотеки отсутствуют.

нужно вручную в коде прописывать загрузку/выгрузку библиотек и поиск нужных функций;

компилятор не может проверить типы и количество передаваемых параметров в загружаемые таким образом функции.

❗
➕
➕
➕
➕
➖
➖
➖
➖
❗
❗
➕
➕
➖
➖
➕
➕
➕
➖
➖
подключение внешних библиотек
импорта в приложение
экспорта из библиотеки DLL