В моем предыдущем посте Программирование в C— статических библиотеках(https://medium.com/@novellichris/programming-in-c-static-libraries-609fef8c9779) мы рассмотрели идею и определение того, что представляет собой библиотека как в общем смысле, так и в том, как это применимо к программированию (даже более конкретно — к C языку программирования). Поэтому в этом посте предполагается, что вы знакомы с тем, что такое C библиотека для программирования, и с сопутствующим процессом компиляции — если нет, перед тем как продолжить, ознакомьтесь с вышеупомянутым постом, на который есть ссылка. При этом мы еще раз обсудим почему мы используем библиотеки (в целом) и как они работают, прежде чем углубиться в основную тему этого поста — динамические библиотеки — и как они сравниваются/сопоставляются с — статическими библиотеками, в том, что касается: использования, создания, реализации и т. д.

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

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

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

Теперь, когда вы это упомянули, что такое статическаябиблиотека ?

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

Круто, а что такое динамическая (общая) библиотека?

Динамическая (общая) библиотека – это файл, который опять-таки представляет собой набор файлов возражений, содержащих определенные функции (и/или прототипы функций), которые мы можем/будем вызывать в нашей C программе. Здесь начинаются различия — на динамическую библиотеку ссылаются использующие ее программы — во время времени выполнения. Кроме того, программа, использующая общую библиотеку только, ссылается на код, который ей необходимо использовать, в указанной библиотеке. Это очень важно и резко отличается от общих библиотек, которые связываются со всем файлом библиотеки во время компиляции, поэтому вся библиотека становится частью файла. Они сохраняются как файлы .so, так как по сути являются общим объектным кодом.

Каковы преимущества/недостатки (из-за внутренних различий) между статическими и динамическими библиотеками?

Во-первых, размер и объем памяти

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

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

Преимущество: динамический

Во-вторых, время выполнения

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

Динамический: быстрее, потому что код общей библиотеки уже находится в памяти.

В-третьих, Совместимость:

Типичная статическая библиотека не столкнется со многими проблемами совместимости, потому что любые изменения

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

Преимущество: статический

Итак, как работают библиотеки?

Это работает, потому что мы связываемся с библиотекой, включив ее в начало нашей Cprogram.

Например, чтобы использовать функцию printf() для записи чего-либо в ваш стандартный вывод (терминал — чтобы увидеть это на экране), вам потребуется включить библиотеку <stdio.h> в файл .c.

Это будет выглядеть примерно так:

#include <stdio.h>

int main void()

{

printf();

return (0);

}

Ключом в приведенном выше примере является строка #include, которая является директивой препроцессора. Это говорит компилятору искать файл с заданным именем stdio.h в стандартном списке системных файлов/каталогов. Фактически это то же самое, что и копирование содержимого заголовочного файла, обозначаемого расширением .h.

Это обеспечит необходимые объявления и определения функций, чтобы использовать printf() в вашем файле .c, связываясь с ним на последнем этапе процесса компиляции, эффективно делая это .h файл, часть исполняемой версии вашей программы — a.out

Если бы вы не включили это, вы бы получили ошибку в строках

error: implicit declaration of function printf()

Это в основном говорит о том, что программа не знает, что делать с функцией printf(), потому что, хотя вы ее объявили, она не была определена — отсюда и включение библиотеки stdio.h (заголовочный файл), где она определена и поэтому можно назвать/использовать.

Итак, как мне создать статическую библиотеку? — Linux

Рад, что вы спросили. Базовая команда, используемая для создания статической библиотеки, вызывает программу ar, что означает archiver, и вот она, наконец, (как я упоминал и ссылался на несколько раз). Поскольку статические библиотеки на самом деле представляют собой архивныефайлы, их необходимо индексировать, чтобы компилятор мог быстрее и эффективнее определить расположение необходимых ему объектных файлов в вашем архивный файл. По сути, он предоставляет компилятору оглавление.

По сути, вы должны вызывать эту команду при именовании объектных файлов, содержащих определенные функции и/или прототипы функций (как описано выше), которые вы хотите #include добавить в свою библиотеку, также известную как архив. файл.

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

Для этого базовый формат будет выглядеть следующим образом:

gcc -c file1.c file2.c file3.c

gcc -c *.c

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

Это создаст (объектный файл) .o версий заданных .cфайлов, и теперь вы можете создать свою статическую библиотеку, как показано ниже.

Основной формат команды выглядит так:

ar options libraryname objectfile1 objectfile2 objectfile3

Если вы хотите создать библиотеку о Короле Льве, это будет выглядеть так:

ar rc liblionking.a simba.o nala.o timon.o pumba.o mufasa.o sarabi.o

Убедитесь, что вы не включили scar.o — если вы это сделаете, (фильм почти 23-летней давности — предупреждение о спойлере), это убьет Муфасу, и вам придется убегать…убегать и никогда не возвращаться '. Вы же не хотите закончить как Симба, не так ли?

Эта команда создает статическую библиотеку с именем liblionking.a, которая теперь содержит копии указанных выше объектных файлов.

И последний важный момент — в отношении вышеупомянутого процесса индексации вы можете сделать это одним из двух способов. Либо указать опцию -s при вызове команды ar

ar -s libname objfile1 objfile2

OR

Используйте команду, известную как ranlibПОСЛЕ — вы создали файл библиотеки .a. Это создаст индекс (оглавление) объектных файлов, присутствующих в вашем архиве.

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

Милая, а как насчет создания общей (динамической) библиотеки? — Linux — я слышал, что они сейчас в моде

foo.c:

#include <stdio.h>

void foo(void)

{

puts("Hello, I'm a shared library");

}

основной.с:

#include <stdio.h>

#include "foo.h"

int main(void)

{

puts("This is a shared library test...");

foo();

return 0;

}

Первый шаг в создании разделяемой (динамической) библиотеки — это вызов команды gcc с несколькими флагами, в частности одним из них: -fPIC.

$ gcc -c -Wall -Werror -fpic foo.c

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

Результатом этой команды является объектный файл или объектные файлы — .o как мы узнали выше (процесс компиляции статической библиотеки). Таким образом, нам нужно использовать наш объектный код для создания фактического файла общего объекта (библиотеки) — .so

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

gcc -shared -o libfoo.so foo.o

-shared имеет решающее значение, поскольку в нем проводится различие между созданием разделяемой (динамической) библиотеки и статической библиотеки.

Теперь с нашей недавно созданной общей библиотекой мы можем скомпилировать наш main.c и связать его с libfoo. Окончательное имя программы будет test. Обратите внимание, что параметр -lfoo не ищет foo.o, а libfoo.so. Это связано с соглашением об именах, где gcc предполагает, что все библиотеки начинаются с lib и заканчиваются на .so или .a — как обсуждалось ранее.

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

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

$ gcc -L/home/username/foo -Wall -o test main.c -lfoo

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

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

Таким образом, четвертый (и последний) шаг заключается в добавлении рабочего каталога к существующему LD_LIBRARY_PATH, чтобы наша программа была исполняемой:

$ export LD_LIBRARY_PATH=/home/username/foo:$LD_LIBRARY_PATH
$ ./test
This is a shared library test...
Hello, I'm a shared library

Это здорово и все такое, но как мне использовать эти статические библиотеки?

Это немного другой процесс по сравнению с использованием предварительно загруженных файлов системной библиотеки, описанных выше. Для этого вам действительно нужно включить конкретную опцию при вызове команды gcc, чтобы скомпилировать файл .c, в котором вы хотите использовать вновь созданную библиотеку — ваш.a файл, и сгенерировать файл .o. Кроме того, вы должны назвать библиотеку, которую хотите включить в файл .c, но в измененном формате — убрать словоlibиз .aимя файла, а также вызовите несколько специальных опций.

Это параметры -L., которые сообщают компоновщику (заключительный этап процесса компиляции), что библиотека может быть найдена в любом указанном вами каталоге, в данном случае . указывает, что она должна искать в текущем каталоге, в дополнение к стандартным системным файлам/каталогам это обычно (как упоминалось ранее). Затем вы должны использовать параметр -l вместо части lib в имени файла, а также опустить расширение .a. Это связано с тем, что компоновщик добавит их обратно, когда будет искать имя файла вашей библиотеки. Наконец, опция -o предназначена для создания файла .o (чтобы сделать ваш файл исполняемым) и позволяет вам назвать его иначе, чем a.out по умолчанию из вашего файла исходного кода .c.

Собрав все вместе, используя нашу более раннюю библиотеку примера liblionking.a и желая использовать ее во вновь созданном файле hakunamatata.c и назвав исполняемый файл .o file‘no_worries’, будет выглядеть так:

gcc hakunamatata.c -L. -llionking -o no_worries

Гипотетически, если бы мы попытались запустить его, и он включил бы известную цитату из фильма в нашу функцию printf, это было бы так:

Запустите исполняемый файл:

./no_worries

Вывод в командную строку:

“Hakuna matata. It means no worries.”

Надеюсь, вы узнали что-то понятное C нет, только мне?