Если ты встал на путь С/С++ разработчика, то скорее всего (помимо использования стандартной библиотеки - libc) рано или поздно вам потребуется занятся разработкой собственных библиотек. Зачем??.. Причин может быть несколько. Например вы написали свою структуру данных или свой алгоритм, и хотите использовать его повторно или распространять. Так же возможно вы написали несколько утилит и все они используют один и тот же кусок кода (например, как часто это бывает, логгер), и будет логично вынести этот кусок кода в отдельный модуль. Поскольку сопровождать такой код будет проще.
Реализация
И так, давайте начнем с примера. Создадим заголовочный файл somecode.h, который будет содержать объявление некоторой функции. Пусть будет простая функция которая разбивает предложение на слова и печатает каждое слово в новой строке. Простой синтетический пример.
И создадим файл somecode.c, в которой напишем реализацию нашей функции.
Далее создадим файл main.c, где будем вызывать нашу функцию.
Давайте для начала скомпилируем это все самым обычным способом для проверки работоспособности.
- из исходных файлов получаем объектные файлы
- из объектных файлов получаем исполняемый файл
Запускаем исполняемый файл и видим, что все работает.
А теперь рассмотрим пример получения библиотеки и линковки его к исполняемому файлу. Для компиляции используем вызов gcc со следующими опциями.
Из файла с расширением .c мы получаем файл с расширением .so. И так же обратите внимание, что библиотека имеет префикс lib. Еще мы видим, что появились два дополнительных аргумента -shared и -fpic. С помощью опции -shared мы говорим компилятору, что хотим получить а выходе библиотеку. А опция -fpic говорит компилятору, что объектные файлы должны содержать позиционно-независимый код (position independent code), который рекомендуется использовать для динамических библиотек.
Теперь скомпилируем наш исполняемый файл подключив к нему нашу библиотеку. Для этого нужно указать название библиотеки через опцию -l.
И опс.. мы получили ошибку… Линкер говорит нам, что он не знает где лежит наша библиотека. С помощью опции -L указываем текущую директорию, где лежит наша библиотека и компиляция проходит успешно.
Пытаемся запустить нашу и программу и ловим еще одну ошибку в котором говорится, что в процессе загрузки динамических библиотек отсуствует наша библиотека.
По умолчанию в операционной системе есть некоторое количество стандартных директорий, где должны располагатся библиотеки. Посмотреть этот список можно так.
Так же есть возможность задавать дополнительные директории с библиотеками с помощью переменной окружения LD_LIBRARY_PATH. С помощью утилиты ldd посмотрим от каких библиотек зависит наша программа.
Добавим в LD_LIBRARY_PATH текущую директорию.
Видим, что наша библиотека подгрузилась.
И теперь программа запускается и работает.
Если сравнить два исполняемых файла, то видим, что программа, которая использует динамическую библиотеку имеет меньший размер.
Это произошло как раз за счет того, что реализация нашей функции теперь лежит за пределами нашего исполняемого файла. С помощью утилиты objdump можем глянуть внутрь бинарных файлов. И увидим, что во втором бинарном файле реализация функции отсутствует, но присутствует в библиотеке.
Разница в нашем случае может и маленькая, но в масштабах десятков и сотен файлов разница будет значительной.
Так что же мы сделали?
У нас есть исходные файлы, мы скомпилировали их, получив из них динамическую библиотеку. Далее эту библиотеку можем использовать повторно в других проектах.
-------------------------------- ---------------------------------
| Заголовочный файл | | Реализация |
| (somecode.h) | | (somecode.c) |
-------------------------------- ---------------------------------
| | | |
| void print_split(char* string);| | void print_split(char* string) |
| | | { |
| | | // ... |
| | | } |
| | | |
------------------------------- ---------------------------------
|
| Компилируем
\ / (gcc -shared -fpic -o liblog.so ....)
|
---------------------------------
| Динамическая библиотека |
| (liblog.so) |
---------------------------------
|
| (gcc ... -llog)
---------------------------------------------
| | |
----------------------- ----------------------- -----------------------
| Утилита1 | | Утилита2 | | Утилита3 |
----------------------- ----------------------- -----------------------
| | | | | |
| #include "somecode.h" | | #include "somecode.h" | | #include "somecode.h" |
| | | | | |
| print_split("..."); | | print_split("..."); | | print_split("..."); |
| | | | | |
----------------------- ----------------------- -----------------------
Примерно те же действия вы будете делать и под windows и под мак, будут немного другие компиляторы, но идея одна.