Когда и зачем нам использовать библиотеки?
Представьте, что вы пишете статью об эволюции и истории черепах на Мадагаскаре. Вы просто хотите сразу погрузиться в сочинение о черепахах и не хотите, чтобы вас беспокоили перефразирование всей теории эволюции Дарвина. Естественно, вы просто вызываете его теорию по имени: «Теория эволюции Дарвина». Кроме того, вы цитируете основополагающую книгу Дарвина «Происхождение видов», чтобы те, кто не знает, что такое «теория эволюции», могут перейти в библиотеку и посмотрите, что на самом деле означают эти три слова в сочетании.
Библиотеки C более или менее построены на той же предпосылке: можно сэкономить много времени и усилий, повторно используя работу, которую кто-то (включая вас в прошлом) уже сделал. Мы также извлекаем выгоду из того факта, что Дарвин, вероятно, сформулировал свою теорию лучше, чем мы, и аналогичным образом коллективное сознание разработчиков, которые работали над чем-то вроде стандартной библиотеки C, скорее всего, написали более надежную функцию для печати строки, чем мы могли дать время. ограничения (по крайней мере, в моем случае).
Библиотека очень похожа на исполняемый файл, но вместо того, чтобы запускаться напрямую, они вызываются по имени из вашей программы с указанными вами параметрами.
Как создать статическую библиотеку в Linux?
Для создания статических библиотек мы используем команду ar
, которая обозначает программу «архиватор». Эта программа может создавать, перечислять и изменять статические библиотеки или «архивные файлы» из нашей командной строки.
Чтобы создать статическую библиотеку для функции, которую мы использовали в нашем примере выше, используйте команду:
ar -r -c libalphabet.a print_alphabet.o
Эта команда создает статическую библиотеку с именем libalphabet.a
и помещает в нее копию файла print_alphabet.o
.
- Флаг
-r
указывает архиватору заменить любые старые объектные файлы новыми объектными файлами. - Флаг
-u
указывает архиватору создать файлlibalphabet.a
, если он не существует.
Но это не все. Мы можем и должны индексировать нашу библиотеку, чтобы компилятор быстрее и эффективнее находил наши связанные программы. Команда для создания индекса нашей библиотеки (или обновления существующего индекса) - это ranlib
, которая принимает нашу библиотеку в качестве аргумента:
ranlib libalphabet
.a
Этот шаг не всегда необходим, поскольку иногда он выполняется автоматически.
Чтобы увидеть содержимое нашей библиотеки, мы можем использовать комбинацию ar -t
. Но если нам нужно больше информации, мы можем использовать команду nm
. Эта команда перечисляет значение, тип и имя каждого символа.
Как создать динамическую библиотеку в Linux?
Чтобы создать динамическую библиотеку, нам сначала нужно скомпилировать все файлы, содержащие желаемые функции для этапа компоновки, используя флаг -fPIC
, который генерирует независимый от позиции код.
gcc -c -fPIC *.c
Затем нам нужно преобразовать этот код объектного кода в общую библиотеку. Вместо использования архиватора, как мы делали с генерацией статической библиотеки, мы используем флаг -shared
в другом прогоне с gcc
. Убедитесь, что вы включили флаг -o
, чтобы дать вашей библиотеке собственное имя. Библиотеке необходимо расширение .so
для общего объекта и, по соглашению, префикс lib
, чтобы указать, что это библиотека. Чтобы включить весь наш объектный код, мы используем подстановочный знак *
, за которым следует расширение .o
(объектный файл).
gcc -shared -o libalphabet.so *.o
Наконец, если вы хотите проверить и увидеть, какие именно функции содержатся в вашей недавно созданной библиотеке, вы можете использовать команду nm
с флагом -D
, который ссылается на символы в разделе инициализированных данных.
nm -D libalphabet.so
В чем основные различия между статическими и динамическими библиотеками?
Статические библиотеки отличаются от динамических (иногда называемых: совместно используемых) библиотек тем, что код статической библиотеки связан непосредственно с вашим окончательным исполняемым файлом.
Динамическое связывание указывает на адрес вашей функции в памяти, а не вставляет объектный код непосредственно в него. Фактический объектный код появляется только тогда, когда программа запущена (во время выполнения).
Статическая компоновка имеет несколько недостатков по сравнению с динамической компоновкой.
- Размер: каждая функция в вашей программе будет иметь объектный код, связанный напрямую. Это эквивалентно копированию и вставке «Происхождение видов» каждый раз, когда вы хотите сказать «теория эволюции». Что, как вы понимаете, сделало бы ваше эссе о черепахах гораздо более громоздким и неэффективным, чем должно быть.
- Обновления: если библиотека, которую вы статически связали, обновлена (для исправления ошибки или чего-то еще), вам необходимо перекомпилировать всю вашу программу.
Рассмотрим функцию print_alphabet()
. Он находится в файле с именем print_alphabet.c
и выглядит примерно так:
#include <stdio.h> void print_alphabet(void) { printf("abcdefghijklmnopqrstuvwxyz"); }
#include <stdio.h>
включает стандартную библиотеку ввода-вывода C, в которой естьprintf()
функция.printf()
- это стандартная библиотечная функция, которая просто печатает строку.
Наша print_alphabet()
функция не делает ничего кричащего; он делает одну вещь и делает это хорошо: печатает алфавит.
Мы можем преобразовать эту программу в объектный код, используя следующую команду:
gcc -c print_alphabet.c
Теперь наш файл print_alphabet.o
и содержит объектный код вместо исходного кода. Этот объектный код вставляется непосредственно в любую программу, которая использует статическую библиотеку, содержащую функцию print_alphabet()
: статическое связывание в двух словах.
Каковы плюсы и минусы статических и динамических библиотек?
Static libraries: ╔════════════════════════════╦══════════════════════════════╗ ║ Pros ║ Cons ║ ╠════════════════════════════╬══════════════════════════════╣ ║ 1. No runtime dependencies ║ 1. Uses lots of memory ║ ║ ║ 2. Needs to be recompiled if ║ ║ ║ updated ║ ╚════════════════════════════╩══════════════════════════════╝ vs. Dynamic libraries: ╔════════════════════════════╦════════════════════════════╗ ║ Pros ║ Cons ║ ╠════════════════════════════╬════════════════════════════╣ ║ 1. Takes up less memory ║ 1. Library must be present ║ ║ ║ in order for program ║ ║ ║ to run ║ ║ 2. Don't need to recompile ║ 2. Less portable ║ ║ main program following ║ ║ ║ an update ║ ║ ╚════════════════════════════╩════════════════════════════╝
Спасибо за прочтение.