Когда мы говорим о библиотеках, на самом деле мы думаем о наборе стопок книг, полных ценной информации, в которых были запечатлены концепции и теории самых блестящих умов в истории.
Хорошо, я немного преувеличиваю, но статические библиотеки — это в чем-то похожая концепция, только мы делаем это с помощью кода, пойдемте со мной, чтобы посмотреть, что из себя представляют все эти библиотечные вещи.
Зачем использовать библиотеки?
В двух словах, библиотеки — это набор объектных файлов или элементов, которые мы можем вызывать из нашей программы и которые можно использовать как единое целое на этапе компоновки программы.
Действительно мощная вещь в использовании библиотек заключается в том, что с их помощью мы можем вызывать различные функции, которые мы сохранили в них, чтобы сделать наш код пригодным для повторного использования.
Мы должны использовать статические библиотеки, когда у нас есть много отдельных файлов, которые будут вызываться в моей программе, и сам факт разделения на несколько файлов сделает процесс компиляции более длительным, чем когда все в одном пакете (библиотеке), откуда компилятор будет искать напрямую.
Кроме того, процесс компоновки программы, объектные файлы которой расположены в библиотеках, выполняется быстрее, чем компоновка программы с отдельными объектными файлами на диске.
Как они работают?
Как мы знаем, в прошлой статье о компиляции я объяснил, по какому пути следует программа, чтобы быть скомпилированной и преобразованной в исполняемый файл, понятный компьютеру.
В этом случае мы увидели, что последней частью процесса компиляции является компоновка, при которой файл принимается в объектном формате, содержащем машинный код.
На этом последнем этапе наш компилятор берет объектный код, сгенерированный на этапе сборки, и объединяет его с имеющимися библиотеками.
Именно там статические библиотеки прилипают к коду на последнем шаге компиляции нашего файла. Таким образом, мы можем скомпилировать наш файл, добавив сгенерированную статическую библиотеку для повторного использования функций, которые мы уже создали ранее.
Итак, если у нас есть, например, 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
Как мы видим, способ, которым мы компилируем наш код, намного быстрее, а также возможность повторного использования кода гораздо проще.
Преимущества и недостатки использования статических библиотек
- Один из больших недостатков статических библиотек заключается в том, что в случае обновления кода функции из-за доработок алгоритма или из-за того, что есть ошибка и мы хотим ее исправить, то мы должны заново сгенерировать библиотеку и как Вы можете себе представить, что нам нужно перекомпилировать наш код.
- Также другой недостаток возникает, когда у вас есть большое количество объектных файлов внутри библиотеки, каждая программа в системе, которая использует эту статическую библиотеку, содержит копию в своем исполняемом файле, что делает ее очень неэффективной.
Чтобы решить эти проблемы, существуют динамические библиотеки, основная характеристика которых заключается в том, что они являются разделяемыми библиотеками и загружаются динамически во время выполнения, когда это необходимо приложению.
До сих пор я надеюсь, что помог немного больше узнать о статических библиотеках и их мощной функциональности при повторном использовании кода.