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

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

Зачем использовать библиотеки?

В двух словах, библиотеки — это набор объектных файлов или элементов, которые мы можем вызывать из нашей программы и которые можно использовать как единое целое на этапе компоновки программы.

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

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

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

Как они работают?

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

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

На этом последнем этапе наш компилятор берет объектный код, сгенерированный на этапе сборки, и объединяет его с имеющимися библиотеками.

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

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

Мы можем создавать статические библиотеки с помощью программы под названием «ar» для каждого архиватора. С помощью этой программы вы можете делать множество вещей: создавать статические библиотеки, перечислять имена объектных файлов в библиотеке, изменять объектные файлы в статической библиотеке и многое другое.

Перед использованием программы «ar» мы должны преобразовать наши функции в объектные файлы, и, как мы видели в предыдущем посте, мы можем сделать это с помощью следующей команды:

gcc -c *.c

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

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

ar rc libmy.a *.o

В этой команде мы видим, что есть определенные параметры, которые мы объясним ниже:

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

Важно отметить, что после создания библиотеки ее необходимо проиндексировать. Компилятор ищет этот индекс, чтобы ускорить поиск символов в библиотеке, и чтобы убедиться, что порядок символов не имеет значения во время компиляции, мы можем использовать команду «ranlib», как показано ниже.

ranlib libmy.a

Как вы уже могли заметить, названия библиотек имеют префикс и суффикс, которые всегда повторяются, это lib и .a соответственно. Таким образом, имя библиотеки как таковой в этом случае будет «моя», и позже мы увидим, как мы можем использовать это имя для более прямого вызова нашей библиотеки.

Чтобы просмотреть содержимое нашей созданной библиотеки, мы можем использовать команду «ar», как показано ниже.

ar -t libmy.a

Мы также можем увидеть символы нашей библиотеки, используя следующую команду:

nm libmy.a

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

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

Во-первых, в качестве примера, что мы сделаем, это создадим пример файла main.c, который будет использовать функцию puts, которая у нас есть внутри нашей статической библиотеки, мы продемонстрируем, как это будет компилироваться без библиотеки и с библиотекой.

Сначала пример без библиотеки

У нас есть файл main.c

И функция puts, показанная ниже

Обычно для компиляции этого файла мы должны добавить файл 3-puts.c и файл main.c.

gcc -Wall -pedantic -Werror -Wextra -std=gnu89 _putchar.c main.c 3-puts.c -o 3-puts

И после компиляции нашего кода и запуска он дает нам следующий результат

В этом случае мы говорим только об одной функции, но давайте представим, что мы используем 10 функций в конкретной программе, тогда мы должны добавить все функции, так как была добавлена ​​функция 3-puts.c, и это затянет процесс компиляции все больше и больше. в то время как больше функций находится в программе.

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

Чтобы скомпилировать и использовать нашу библиотеку, мы сделаем следующее:

gcc -Wall -pedantic -Werror -Wextra -std=gnu89 -o 3-puts main.c -L. libmy.a

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

Также, как мы сказали, мы можем вызвать нашу библиотеку, поместив «-l» перед именем моей библиотеки, и больше не будет необходимости размещать расширение «.a», как показано ниже.

gcc -Wall -pedantic -Werror -Wextra -std=gnu89 -o 3-puts main.c -L. -lmy

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

Преимущества и недостатки использования статических библиотек

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

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

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