Вы наткнулись на этот пост, скорее всего, из-за поиска информации о «Статических библиотеках на C». Добро пожаловать. В Холбертоне второй месяц, и я только что создал свою первую статическую библиотеку. Что это означает? Что ж, давайте разберем это вместе.

Статические библиотеки

Что такое статические библиотеки? Статическая библиотека, также известная как статически связанная библиотека, это:

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

Вы можете подумать: Хорошо… но что это значит?. Чтобы понять это, нам нужно сначала понять процесс компиляции. Если вы посмотрите на мою предыдущую запись в блоге Gcc your .c, я подробно расскажу о процессе компиляции gcc, но давайте сделаем краткое обновление.

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

#This is your program file, which contains code to execute the program called main:
$ main.c

Когда вы вводите команду gcc -c main.c, вы перемещаете файл main.c на этап предварительной обработки, который принимает ваш код и компилирует его в main.s и main.o. Давайте пока забудем о main.s. При создании статической библиотеки мы хотим сосредоточиться на файле main.o, который также известен как объектный файл. Объектный файл — это, по сути, файл, содержащий объектный код или инструкции машинного уровня (двоичные), например:

Ïúíþ^G^@^@^A^C^@^@^@^A^@^@^@^D^@^@^@^@^B^@^@^@ ^@^@^@^@^@^@^Y^@^@^@<88>^A^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@¸^@^@^@^@^@^@^@ 

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

Динамический против статического

Я думал, что существуют только статические библиотеки! Ты подумал неправильно! Во-первых, что такое библиотека в техническом смысле? Библиотека состоит из набора связанных функций для выполнения задачи; например, функция с именем isupper. Имя файла библиотеки всегда начинается с lib и заканчивается на a; например libholberton.a — библиотека под названием holberton. Двигаемся дальше, динамические и статические — что их отличает?

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

Статическая библиотека линкуется компоновщиком (последний шаг процесса компиляции gcc перед выполнением) и включается в исполняемый код. Почему это может быть проблемой, если у вас должно быть много программ? Ну, проблема, с которой вы можете столкнуться при использовании статической библиотеки, заключается в том, что, например, у вас есть 10 программ, связанных с вашей статической библиотекой, что означает, что каждая программа

«результирующий двоичный файл будет включать указанную библиотеку в свой программный двоичный файл» — Дармут.

Это означает огромные исполняемые файлы, так как все включено и должно быть загружено! Вот почему люди используют динамическую библиотеку, которая является общей и не требует прямого включения, чтобы на нее можно было ссылаться. Например: <stdio.h> — это динамическая библиотека, на которую можно ссылаться в вашем заголовочном файле следующим образом:

#include <stdio.h>

Очень просто! Набрав #include <stdio.h>, вы теперь получаете доступ к динамической общей библиотеке, что означает, что вы можете получить доступ к таким функциям, как printf(), без наличия исходного кода программы в вашем каталоге. Ваш процессор будет вам благодарен. Статические библиотеки лучше всего подходят для программ, которые вы написали сами, поскольку они не являются стандартными, но давайте не будем перебарщивать с ними, так как они занимают память и замедляют время загрузки. Теперь, когда вы понимаете, почему мы используем библиотеки и разные типы, я могу показать вам, как создать собственную статическую библиотеку.

Создайте статическую библиотеку

Для создания статической библиотеки вам понадобятся две основные вещи:

  • Заголовочный файл, содержащий все ваши прототипы
  • Ваши .c файлов всех ваших программ, которые вы создали
#This is my directory Project_Ramen:
$ ~/home/Project_Ramen
#Let's ls into the directory:
$ ls
#All my program files, .c, are int the directory as well as my header holberton.h, containing all of my function prototypes.
$ ~/home/Project_Ramen
abs.c isupper.c _putchar.c strlen.c holberton.h

Чтобы создать нашу статическую библиотеку, нам нужно иметь все наши прототипы функций в нашем заголовке, а также скомпилировать все наши файлы .c для получения объектных файлов, .o для создания нашей статической библиотеки. Убедимся, что все наши прототипы находятся в заголовочном файле holberton.h:

#Accessing holberton.h header with vim
$ vim holberton.h
#Content of your header file should look like this:
#ifndef HOLBERTON_H
#define HOLBERTON_H
int abs(int n);
int isupper(int c);
int _putchar(char c);
int strlen(char *s);
#endif /* HOLBERTON_H */
#We have all the function prototypes included along with our header guard which are the three lines starting with "#". A brief definition to the purpose of header guards:
"A header guard is used to ensure your code is included only once and is used to prevent an endless loop to include files if there is a circular dependency between your header files".
------------------------------------------------------------------------------
#Header file is ready and now time to compile all your .c files. To do this, we will use the command gcc -c *.c, which compiles (-c) all the .c files (using the wildcard * to grab all the .c files in the current directory) 
$ gcc -c *.c
#Let's ls into the directory to see what we get:
$ ls
total 10
-rw-rw-r-- 1 ubuntu ubuntu 89 Mar 25 02:44 abs.c
-rw-rw-r-- 1 ubuntu ubuntu 1400 Mar 25 02:47 abs.o
-rw-rw-r-- 1 ubuntu ubuntu 132 Mar 25 02:44 isupper.c
-rw-rw-r-- 1 ubuntu ubuntu 1408 Mar 25 02:47 isupper.o
-rw-rw-r-- 1 ubuntu ubuntu 132 Mar 25 02:44 _putchar.c
-rw-rw-r-- 1 ubuntu ubuntu 1408 Mar 25 02:47 _putchar.o
-rw-rw-r-- 1 ubuntu ubuntu 166 Mar 25 02:44 strlen.c
-rw-rw-r-- 1 ubuntu ubuntu 1464 Mar 25 02:47 strlen.o
-rw-rw-r-- 1 ubuntu ubuntu 162 Mar 25 02:44 holberton.h
#As you can see, each .c file is now associated with a .o file, which are your object code files.

Хорошо, у нас есть все наши .o объектных файла, теперь нам нужно создать статическую библиотеку. Как мы это делаем? Нам нужно их заархивировать с помощью команды ar. Команда ar создает, изменяет и извлекает archives (статические библиотеки), также известные как «архиваторы», для архивирования объектных файлов. Например:

#I am in my Project_Ramen
$~/home/Project_Ramen
#ls into my directory which contains my .c files, .o files as well as my header
$ls
abs.c abs.o isupper.c isupper.o _putchar.c _putchar.o strlen.c strlen.o holberton.h
#Using the ar command to create your static library called libholberton.a
$ ar -rc libholberton.a *.o
#Overview of the options with the ar command
1. -r --> option to insert the files into the archive
2. -c --> option to create the archive silently. Without the -c option, an informational message is written in standard error when using ar.
#The command above is telling the computer to create a archive called libholberton.a with all the .o files in the current directory.

Теперь мы создали статическую библиотеку libholberton.a, содержащую все программы нашего проекта! А что, если мы хотим использовать статическую библиотеку?

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

Прежде чем использовать нашу статическую библиотеку, давайте проверим, что там есть:

#Using ar command with option -t to see what files my libholberton.a library includes
$ ar -t libholberton.a
abs.o
isupper.o
_putchar.o
strlen.o
#Remember we created our static library by archiving our .o files? As you can see, when we list the archive, you can see all the object files. 
#If you want to display the symbolic information in your library, you can use the nm command like so:
$ nm libholberton.h
#Your output would be something like this:
abs.o:
0000000000000000 T abs
isupper.o:
0000000000000000 T isupper
_putchar.o:
0000000000000000 T _putchar
                 U write

0-strlen.o:
0000000000000000 T strlen
#The 0’s are hex digits to show you the number of bytes of each program. I am running Ubuntu Linux 64-bit, which is why the 8 digit hex is 16. To check the output, you can do objdump -td abs.o.

После проверки содержимого статической библиотеки попробуем запустить ее:

#Before we run the library, we need to index it. Why? The index is "used by the compiler to speed up symbol-lookup inside the library and to make sure the order of the symbols won't matter during compilation". - docencia
#To create the index, we will use the ranlib command
$ ranlib libholberton.a
#Depending on some systems, indexing may be already done by the archiver (not necessary with ar).
#The ranlib command generates an index to the contents of an archive and stores it in the archive. The index lists each symbol defined by a member of an archive that is a relocatable object file. - ranlib manual page.
------------------------------------------------------------------------------
#After indexing, let's run it using the gcc command on a main.c file.
$ main.c

int main(void)
{
    char c;

    c = 'A';
    printf("%c: %d\n", c, _isupper(c));
    c = 'a';
    printf("%c: %d\n", c, _isupper(c));
    return (0);
}
#gcc command on main.c
$ gcc main.c -L. -lholberton -o isupper
#Compiling the main.c file with the static library holberton and outputting it into the executable isupper.
1. -L --> option to tell gcc to search for a library in the specified directory
2. -l --> option to tell gcc to search in a specific library. In this case, holberton. We don't need to add "lib" or ".a" since the -l option shortens the name and automatically prefixes "lib" and ".a". l for lib and holberton for the library's name
3. -o --> output the executable to a specific name, isupper
------------------------------------------------------------------------------
#Running the isupper program
$./isupper
A: 1
a: 0
#We've successfully ran the isupper program on our main.c file.