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

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

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

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

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

Далее будет приведен пример создания и использования библиотеки при разработке программы.

Пример создания библиотеки

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

После создания библиотека и проект будут расположены следующим образом:

Схема проекта с библиотекой

Пусть каталоги library и project расположены в одном общем директории, например, в домашней папке пользователя. В каталоге library будет находиться каталог source с исходными файлами кода библиотеки, также здесь будут заголовочные файлы с описанием функций библиотек, и сами библиотеки - статическая (libmy1.a) и динамическая (libmy2.so). В каталоге project будут храниться исходные файлы проекта и заголовочный файл его функций. Их компиляция с использованием библиотеки создаст исполняемый файл проекта.
В системах GNU/Linux файлы библиотек должны начинаться с префикса "lib", расширение статических библиотек — *.a, динамических — *.so.

Для компиляции проекта можно использовать либо статическую, либо динамическую библиотеку. Чтобы понять разницу, создадем обе версии, начнем с компиляции со статической библиотекой, затем — с динамической. Хотя обе библиотеки изначально должны иметь одинаковое имя (различие лишь в расширении), для ясности они будут названы по-разному (libmy1 и libmy2).

Исходный код библиотеки

Файл figure.c:

void rect (char sign, int width, int height) {
	int i, j;
 
	for (i=0; i < width; i++) putchar(sign);
	putchar('\n');
 
	for (i=0; i < height-2; i++) {
		for (j=0; j < width; j++) {
			if (j==0 || j==width-1) putchar(sign);
			else putchar(' ');
		}
		putchar('\n');
	}
 
	for (i=0; i < width; i++) putchar(sign);
	putchar('\n');
}
 
void diagonals (char sign, int width) {
	int i, j;
 
	for (i=0; i < width; i++) {
		for (j=0; j < width; j++) {
			if (i == j || i+j == width-1) putchar(sign);
			else putchar(' ');
		}
		putchar('\n');
	}
    }

Файл figure.c содержит две функции: rect() и diagonals(). Первая принимает символ и два параметра - ширину и высоту, и рисует прямоугольник с заданными размерами. Вторая функция выводит диагонали квадрата, формируя крестик.

Файл text.c:

void text (char *ch) {
	while (*ch++ != '\0') putchar('*');
	putchar('\n');
    }

В файле text.c определена функция, с параметром — указателем на символ в строке. Она выводит на экран звездочки в количестве, соответствующем длине этой строки.

Файл mylib.h:

void rect (char sign, int width, int height);
void diagonals (char sign, int width);
    void text (char *ch);

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

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

Процесс создания статической библиотеки проще — она компонуется из объектных файлов посредством утилиты ar.

Все шаги ниже выполняются в директории library, в которой необходимо находиться, например, выполнив команду cd. Чтобы просмотреть содержимое каталога, используйте команду ls или ls -l.

Компиляция объектных файлов:

gcc -c ./source/*.c

В результате в каталоге library должны появиться следующие файлы:

figures.o  mylib.h  source  text.o

Для создания статической библиотеки применим утилиту ar:

ar r libmy1.a *.o

Ключ r добавляет файлы в архив, создавая его, если он отсутствует. Далее приводится имя архива и перечень файлов для архивации.

Удалим объектные файлы, теперь бесполезные:

rm *.o

Содержимое каталога library теперь:

libmy1.a  mylib.h  source

где libmy1.a — это созданная статическая библиотека.

Создание динамической библиотеки

Динамические библиотеки требуют специальной компиляции объектных файлов с позиционно-независимым кодом (position independent code), благодаря чему библиотека загружается в память вместе с программой. Поскольку программа и библиотека не рассматриваются как единое целое, они могут иметь независимое расположение в памяти. Для компиляции объектных файлов с целью создания динамической библиотеки в gcc используется опция -fPIC:

gcc -c -fPIC source/*.c

Роль gcc в создании динамической библиотеки отличается — нужно добавить опцию -shared:

gcc -shared -o libmy2.so *.o

Объектные файлы вновь можно удалить:

rm *.o

Теперь каталог library содержит:

libmy1.a  libmy2.so  mylib.h  source

Использование библиотеки в программе

Исходный код программы

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

Файл data.c:

#include <stdio.h>
#include "../library/mylib.h"
 
void data (void) {
	char strs[3][30];
	char *prompts[3] = {"Ваше имя: ", "Местонахождение: ", "Пунк прибытия: "};
	int i;
 
	for (i=0; i<3; i++) {
		printf("%s", prompts[i]);
		gets(strs[i]);
	}
 
	diagonals('~', 7);
 
	for (i=0; i<3; i++) {
		printf("%s", prompts[i]);
		text(strs[i]);
	}
    }

Функция data() собирает информацию от пользователя, помещая ее в массив strs. Затем вызывает функцию diagonals(), предоставляя визуальный крестик. Каждый элемент массива передается в функцию text(), которая выводит соответствующее число звездочек для каждой строки.

При подключении заголовочного файла библиотеки применяется относительный путь. Две точки обозначают переход в родительский каталог по отношению к project, после чего каталог library входит в путь. Можно задать и абсолютный путь, такой как "/home/sv/c/les_21/library/mylib.h", но в случае перемещения каталогов адрес может стать некорректным. Использование относительного пути требует лишь соблюдения структуры файлов относительно друг друга.

Файл main.c:

#include <stdio.h>
#include "../library/mylib.h"
#include "project.h"
 
main () {
	rect('-',75,4);
	data();
	rect('+',75,3);
    }

Функция rect() вызывается дважды, а data() — из другого файла проекта. Чтобы main() "узнала" о функции data(), применяется заголовочный файл проекта.

В project.h упоминается только:

void data (void);

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

gcc -c *.c

Каталог project должен быть текущим для выполнения команд!

Компиляция проекта со статической библиотекой

Теперь в каталоге project есть main.o и data.o. Они должны быть скомпилированы в исполняемый файл, использующий статическую библиотеку libmy1.a. Команда для компиляции:

gcc -o project *.o -L../library -lmy1

Начало команды объясняется легко: -o указывает на создание исполняемого файла project из объектных файлов.

Участие библиотеки в компиляции указывается опцией -L../library -lmy1. Здесь -L сообщает путь к библиотеке, за которой следуют ее файлы. Опция -l определяет имя библиотеки -(с удалением префикса lib и суффикса (.a или .so)), без пробела после опций.

Не обязательно указывать опцию -L, если библиотека находится в директориях стандартных системных библиотек,例如 /lib/或者/usr/lib/и пр.

При запуске исполняемого файла project на экране отобразится следующее:

Выполнение готовой программы

Размер файла project:

sv@seven:~/c/les_21/project$ ls -l project
-rwxrwxr-x 1 sv sv 8698 2012-04-03 10:21 project

Размер составляет 8698 байт.

Компиляция проекта с динамической библиотекой

Удалим исполняемый файл и создадем новый, связанный уже с динамической библиотекой. Команда для такой компиляции:

gcc -o project *.o -L../library -lmy2 -Wl,-rpath,../library/

Добавлены опции для линковщика: -Wl,-rpath,../library/. -Wl — обращение к линковщику, -rpath — его опция, ../library/ — значение опции. Иными словами, местоположение библиотеки указывается дважды: с -L и -rpath. Видимо, это связано с более глубоким изучением процесса компиляции и компоновки программ на языке C.

Следует заметить, что при использовании данной команды, исполняемый файл будет работать только в текущем каталоге project. Если сменить директорию, возникнет ошибка, так как динамическая библиотека не будет найдена. Однако если использовать абсолютный адрес для линковщика:

gcc -o project *.o -L../library -lmy2 -Wl,-rpath,/home/sv/c/les_21/library

, файл будет запускаться в любом месте системы.

Размер исполняемого файла проекта, связанного с динамической библиотекой, составил 8604 байта, что меньше по сравнению с статической библиотекой.

sv@seven:~/c/les_21/library$ ls -l libmy*
-rw-rw-r-- 1 sv sv 3624 2012-04-02 10:54 libmy1.a
-rwxrwxr-x 1 sv sv 7871 2012-04-02 11:16 libmy2.so

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

Вопросы для самопроверки:

  1. Какие два типа библиотек существуют в языке C и в чем их ключевое различие?
  2. Что содержится в заголовочном файле библиотеки и почему он важен?
  3. Какой утилитой создается статическая библиотека в C и какие шаги предшествуют этому?
  4. Опиши процесс создания динамической библиотеки. Какие дополнительные опции необходимы при компиляции?
  5. Каковы преимущества использования динамической библиотеки по сравнению со статической?

Программа курса:

  1. Описание курса
  2. Введение в язык программирования C
  3. Типы данных в C и форматированный вывод
  4. Символьные типы и управляющие символы в C
  5. Операторы ветвления и switch в C
  6. Циклы и операторы в языке C
  7. Битовые операции в языке C
  8. Посимвольный ввод и вывод в C - буферизация
  9. Переменные, адреса и указатели в C
  10. Передача аргументов по ссылке и значению в C
  11. Форматированный ввод данных с использованием scanf
  12. Генерация псевдослучайных чисел на C
  13. Адресная арифметика в массивах C
  14. Передача массивов в функции и указатели
  15. Строки в языке C - особенности и функции работы
  16. Функции работы со строками в C
  17. Работа со структурами в C - создание и применение
  18. Динамические структуры данных в C
  19. Ввод и вывод данных из файлов в языке C
  20. Передача аргументов в C и работа с файлами
  21. Препроцессор в языке C - директивы и макросы
  22. Создание и компиляция многофайловых программ в C
  23. Использование статических и динамических библиотек в C