Программная библиотека — это набор предварительно скомпилированных подпрограмм или модулей, используемых в программах [1]. Эти модули обычно представляют собой предварительно написанный код, функции, классы, сценарии и т. д., где разработчик использует их для добавления функций или автоматизации процесса без необходимости написания кода с нуля. Библиотеки вызываются и используются в исходном коде, не будучи в нем определены, и компилятор добавляет их в процессе компиляции [2].

Почему важно использовать библиотеки?

Языки программирования, такие как C, предоставляли необходимые основные функции. Этот упомянутый язык даже не содержал функций ввода-вывода (I/O) для чтения с клавиатуры и вывода на экран. В 60-х и 70-х разработчикам приходилось реализовывать свои собственные библиотеки, которые выполняли общие процедуры — обработку строк, печать сообщений, выполнение математических операций, обработку структур данных, манипулирование временем и т. д. Но в 1989 году ANSI разработал Стандартную спецификацию языка C, названную C89, одним из шагов которой было создание Стандартной библиотеки. В 1999 году в эту библиотеку были добавлены некоторые функции в Стандарте C99.

Разработка на языке C увеличилась с внедрением стандартной библиотеки по следующим причинам [3]:

  • Его функции (например, printf и atoi) работают должным образом, поскольку они были тщательно протестированы, а их код был написан наиболее эффективным способом для обеспечения максимально возможной производительности.
  • Это сэкономило время разработки, избегая написания общих базовых процедур.
  • Его функции являются переносимыми, что означает, что они могут быть реализованы на любом компьютере, на котором запущена разработанная программа.

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

Как работают библиотеки?

Библиотеки делятся на два типа: статические и динамические.

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

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

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

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

Предположим, что есть точка входа, которая вызывает функцию с именем show_message, которая выводит на экран Hello world:

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

Затем show_message предварительно компилируется (преобразуется в машинный код) и сохраняется в библиотеке:

Точка входа тоже компилируется. show_message присоединяется к исходному коду через компоновщик. Но в отличие от статической библиотеки машинный код show_message не добавляется в исполняемый файл:

Вместо этого библиотека загружается в память, а адрес вызываемой функции добавляется в исполняемый файл:

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

В Unix динамические библиотеки называются общими библиотеками.

Как их создать?

Процесс создания статических библиотек описан в прошлом блоге. С другой стороны, есть 2 шага для создания динамических библиотек:

  1. Создайте объектный код.
  2. Создать библиотеку.

Предположим, что в файле с именем main.c есть точка входа, которая вызывает функцию calculator:

#include "header.h"
#include <stdio.h>
/**
 * main - Entry Point
 *
 * Return: Always 0.
 */
int main(void)
{
    printf("4 + 2 = %d\n", calculator('+', 4, 2));
    printf("4 - 2 = %d\n", calculator('-', 4, 2));
    printf("4 * 2 = %d\n", calculator('*', 4, 2));
    printf("4 / 2 = %d\n", calculator('/', 4, 2));
    return (0);
}

Функция calculator возвращает результат математической операции в соответствии со значением переданной переменной char:

#include "header.h"
/**
 * calculator - Executes basic math operations
 *
 * @op: char that indicates math operation executed.
 * @num1: number one.
 * @num2: number two.
 * Return: addition if op is '+'
 *         subtraction if op is '-'
 *         multiplication if op is '*'
 *         division if op is '/'
 *         otherwise, 0.
 */
int calculator(char op, int num1, int num2)
{
    if (op == '+')
        return (num1 + num2);
    if (op == '-')
        return (num1 - num2);
    if (op == '*')
        return (num1 * num2);
    if (op == '/')
        return (num1 /num2);
    return (0);
}

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

#ifndef __HEADER__
#define __HEADER__
int calculator(char op, int num1, int num2);
#endif

Итак, созданы следующие файлы:

$ ls
calculator.c  header.h  main.c
$

Для создания объектного файла calculator.c необходимо использовать компилятор GCC:

$ gcc -Wall -fPIC -c calculator.c
$ ls
calculator.c  calculator.o  header.h  main.c
$

Используемые флаги: -Wall, который печатает все предупреждающие сообщения, и -c, который создает объектный файл .o. Флаг -fPIC генерирует независимый от позиции код, который обращается ко всем постоянным адресам, хранящимся в таблице глобальных смещений (GOT). Эта таблица используется исполняемыми программами для поиска во время выполнения адресов функций — calculator в этом примере — и глобальных переменных, неизвестных во время компиляции. Динамический загрузчик, являющийся частью операционной системы, разрешает записи GOT при запуске программы[4].

После создания объектных файлов — calculator.o в этом примере — создается библиотека. Можно использовать следующую командную строку:

$ gcc -shared -Wl,-soname,libcalc.so -o libcalc.so *.o
$ ls
calculator.c  calculator.o  header.h  libcalc.so   main.c
$

Флаг -shared создает общий объект, который в момент компиляции с другим объектным файлом может быть связан для формирования исполняемого файла. -Wl,options передает параметры компоновщику: в данном случае -soname, чтобы указать бинарную совместимость API библиотеки (по умолчанию 0). Флаг -o сохраняет выходную библиотеку в файл с заданным именем — libcalc.so.

Как их использовать?

Шаги по использованию статической библиотеки описаны в прошлом блоге. Для использования динамической библиотеки необходимо экспортировать переменную окружения LD_LIBRARY_PATH, которая имеет значение первой искомой библиотеки исполняемым файлом:

$ export LD_LIBRARY_PATH=$PWD/libcalc.so:
$

Как показано, библиотека находится в текущем рабочем каталоге — $PWD. Затем точка входа — main.c — компилируется с созданной библиотекой:

$ gcc -Wall -pedantic -Werror -Wextra -L. 0-main.c -lcalc
$ ls
calculator.c  calculator.o  header.h  libcalc.so  a.out  main.c
$

С помощью -Lflag указывается каталог, в котором находится созданная библиотека. С другой стороны, с флагом -l имя библиотеки указывается компилятору.

В тот момент, когда запускается a.out — имя по умолчанию, заданное компилятором, этот исполняемый файл находит адрес calculator и выводит результат:

$ ./a.out
4 + 2 = 6
4 - 2 = 2
4 * 2 = 8
4 / 2 = 2
$

Различия между статическими и динамическими библиотеками

Хотя оба они полезны, между ними есть некоторые различия:

ИСПОЛЬЗОВАННАЯ ЛИТЕРАТУРА

[1] ВЕБОПЕДИА. Библиотека. Ссылка: https://www.webopedia.com/TERM/L/library.html

[2] ТЕХНОПЕДИЯ. Библиотека программного обеспечения. Ссылка: https://www.techopedia.com/definition/3828/software-library

[3] ПРОГРАММ. Стандартные библиотечные функции C. Ссылка: https://www.programiz.com/c-programming/library-function

[4] ССЗ. Варианты соглашений о генерации кода. Ссылка: https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html