Как продолжить свое продвижение через Holberton, чтобы стать инженером-программистом и вообще с «C» в качестве первой ноги. Я достиг точки, когда все функции, которые я закодировал до этого момента, могут использовать свою библиотеку, что направляет меня к теме этой статьи.

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

Так что правильным местом для начала было бы определение того, что такое библиотека — для краткости lib — в данном контексте, будь то статическая или общая. Как следует из названия, библиотека в данном случае представляет собой файл, содержащий несколько объектных файлов, которые обеспечивают единую сущность на этапе связывания программы — подробнее об объектных файлах и этапе связывания позже. Как упоминалось выше, в Unix и большинстве современных операционных систем существует два типа библиотек: Static и Shared, также известные как Dynamic. Чем они отличаются, так это тем, как они связаны с приложением.

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

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

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

gcc main.c _putchar.o _strlen.o _puts.o _memcpy.o ... -o some_app

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

Если бы я гипотетически создавал небольшую программу, использующую более 25 пользовательских функций, во-первых, я, вероятно, не хотел бы, чтобы все 25+ были внутри одного источника. Возможно, только 5–10 предназначены для работы с каким-то типом данных, а остальные — для математики — или w/e. Даже тогда у нас могут быть функции для этого воображаемого типа данных, разбитые на подмножество файлов для организационных целей. Тем не менее, вы представляете себе сценарий, при котором компиляция будет подвержена человеческим ошибкам или будет иметь комично большую длину команды.

Там, где раньше было, возможно, 25 записей, теперь стало 1 или 2 библиотеки — основываясь на гипотетическом сценарии выше.

Как его создать?

Давайте посмотрим на фазу создания на примере варианта использования.

Как упоминалось несколько раз, Static lib представляет собой архив объектных файлов. Создание объекта относительно просто, используя gcc, достаточно добавить флаг -c, а затем исходные файлы. При использовании этой опции выводятся файлы с тем же именем, но с расширением .o вместо .c .

$ ls
some_src.c header.h main.c
$ gcc -c some_src.c
$ ls
some_src.c some_src.o header.h main.c
$ gcc main.c some_src.o -o dose_something

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

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

$ gcc -c *.c # create object
$ ar -rc libmimic.a *.o # add objects to lib

Переходя к шагам, во-первых, нам нужно скомпилировать наши исходные файлы, используя gcc

$ gcc -c *.c
$ ls
0-isupper.c
0-isupper.o
0-memset.c
0-memset.o
0-strcat.c
0-strcat.o
100-atoi.c
100-atoi.o
.
.
.
$

После компиляции всего исходного кода в машинный код мы можем использовать инструмент ar — архив — для их упаковки. Ну и сопровождайте ‘ar’ некоторыми опциями -rc

  • c: создает архив.
  • r: вставьте элементы файла в созданный архив

libmimic.a это имя или архив и *.oиспользовать весь объект, который мы только что скомпилировали.

Инструмент ar также поставляется с некоторыми полезными опциями для вывода списка всех объектов, которые в данный момент находятся в архиве -t , а также есть еще один инструмент nm для печати списка символов объектных файлов внутри архива.

Использование ar

$ ar -t libmimic.a # View objects within a lib using the '-t'
0-isupper.o
0-memset.o
0-strcat.o
100-atoi.o
.
.
.
$

Использование nm

$ nm libmimic.a
0-isupper.o:
0000000000000000 T _isupper
0-memset.o:
0000000000000000 T _memset
0-strcat.o:
0000000000000000 T _strcat
.
.
.
$

Как использовать статическую библиотеку?

Использование созданной нами библиотеки достаточно просто, но есть небольшая проблема, которую стоит продемонстрировать. Ниже приведен код для main.c, который мы будем использовать с lib.

Я согласен, что статическая библиотека может оказаться излишней для этого примера, но пошутите.

#include "mimic.h"
int main(void)
{
    _puts("\"At the end of the day, my goal was to be the best   hacker\"\n\t- Kevin Mitnick");
    return (0);
}

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

$ ls
_header  _libs  main.c  README.md  _src
$ gcc main.c -I ./_header -L ./_libs -l mimic -o quote
$ ./quote
"At the end of the day, my goal was to be the best hacker"
        - Kevin Mitnick
$

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

$ gcc ... -Iinclude_path ...

Далее следует параметр -L./_libs, указывающий, где находится директор библиотек, в данном случае _libs, а затем -lmimic это указывает gcc, какую библиотеку использовать для связывания с программой. Обратите внимание, что префикс «lib» и суффикс «.a» не включены.

$ gcc ... -L ./_libs -l mimic ...

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

Общие библиотеки

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

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

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

Мои источники