Как подключить библиотеку в проекте на С++
При использование Visual Studio: самый простой — в любом файле добавить запись:
#pragma comment(lib, "")
Как альтернатива, можно указать lib-файл в свойствах проекта, для этого перейдите к пункту:
- Linker → General → Additional Library Directories — указать каталог с lib-файлов.(напр. D:\ace\lib)
- Linker → Input → Additional Dependencies — указать само название lib файла (напр. ace_vc11.lib)
Так же в C/C++ → General → Additional Include Directories можно указать путь к *.h файлам, чтоб в своих исходниках не прописывать полный путь на диске.
Update:
Если библиотека из себя представляет только h-файл(такое возможно), тогда достаточно просто написать:
#include ""
и далее пользоваться предоставленным функционалом.
Но в основном библиотека представляет из себя *.lib -файл и *.h -файлы, необходимые для сборки своего приложения а также непосредственно *.dll -файл, необходимый для запуска приложения.
Как написать свою библиотеку на Си
Если ты встал на путь С/С++ разработчика, то скорее всего (помимо использования стандартной библиотеки — libc) рано или поздно вам потребуется занятся разработкой собственных библиотек. Зачем. Причин может быть несколько. Например вы написали свою структуру данных или свой алгоритм, и хотите использовать его повторно или распространять. Так же возможно вы написали несколько утилит и все они используют один и тот же кусок кода (например, как часто это бывает, логгер), и будет логично вынести этот кусок кода в отдельный модуль. Поскольку сопровождать такой код будет проще.
Реализация
И так, давайте начнем с примера. Создадим заголовочный файл somecode.h, который будет содержать объявление некоторой функции. Пусть будет простая функция которая разбивает предложение на слова и печатает каждое слово в новой строке. Простой синтетический пример.
1 2 3 4 5 6
#ifndef __SOMECODE__ #define __SOMECODE__ void print_split(char* str); #endif
И создадим файл somecode.c, в которой напишем реализацию нашей функции.
1 2 3 4 5 6 7 8 9 10 11 12
#include "somecode.h" #include #include void print_split(char* str) const char *word = strtok(str, " "); while (word != NULL) printf("> %s\n", word); word = strtok(NULL, " "); > >
Далее создадим файл main.c, где будем вызывать нашу функцию.
1 2 3 4 5 6 7 8 9 10
#include #include "somecode.h" int main(int argc, char** argv) if (argc > 1) print_split(argv[1]); > >
Давайте для начала скомпилируем это все самым обычным способом для проверки работоспособности.
- из исходных файлов получаем объектные файлы
- из объектных файлов получаем исполняемый файл
1 2 3 4 5 6 7 8 9 10 11 12
$ gcc -c -Wall -g -o somecode.o somecode.c $ gcc -c -Wall -g -o main.o main.c $ gcc -Wall -g -o a.out.1 main.o somecode.o $ ls -l $ ls -l total 40 -rwxr-xr-x 1 ainr ainr 19816 Aug 9 21:22 a.out.1 -rw-r--r-- 1 ainr ainr 123 Aug 9 21:20 main.c -rw-r--r-- 1 ainr ainr 3576 Aug 9 21:22 main.o -rw-r--r-- 1 ainr ainr 213 Aug 9 21:21 somecode.c -rw-r--r-- 1 ainr ainr 72 Aug 9 21:20 somecode.h -rw-r--r-- 1 ainr ainr 6224 Aug 9 21:21 somecode.o
Запускаем исполняемый файл и видим, что все работает.
1 2 3 4 5
$ ./a.out.1 "hello my little pony" > hello > my > little > pony
А теперь рассмотрим пример получения библиотеки и линковки его к исполняемому файлу. Для компиляции используем вызов gcc со следующими опциями.
$ gcc -Wall -g -shared -fpic -o libsomecode.so somecode.c
Из файла с расширением .c мы получаем файл с расширением .so. И так же обратите внимание, что библиотека имеет префикс lib. Еще мы видим, что появились два дополнительных аргумента -shared и -fpic. С помощью опции -shared мы говорим компилятору, что хотим получить а выходе библиотеку. А опция -fpic говорит компилятору, что объектные файлы должны содержать позиционно-независимый код (position independent code), который рекомендуется использовать для динамических библиотек.
Теперь скомпилируем наш исполняемый файл подключив к нему нашу библиотеку. Для этого нужно указать название библиотеки через опцию -l.
1 2 3
$ gcc -Wall -g -o a.out.2 main.c -lsomecode /usr/bin/ld: невозможно найти -lsomecode collect2: error: ld returned 1 exit status
И опс.. мы получили ошибку… Линкер говорит нам, что он не знает где лежит наша библиотека. С помощью опции -L указываем текущую директорию, где лежит наша библиотека и компиляция проходит успешно.
$ gcc -Wall -g -o a.out.2 main.c -lsomecode -L.
Пытаемся запустить нашу и программу и ловим еще одну ошибку в котором говорится, что в процессе загрузки динамических библиотек отсуствует наша библиотека.
$ ./a.out.2 "hello my little pony" ./a.out.2: error while loading shared libraries: libsomecode.so: cannot open shared object file: No such file or directory
По умолчанию в операционной системе есть некоторое количество стандартных директорий, где должны располагатся библиотеки. Посмотреть этот список можно так.
1 2 3 4 5 6 7 8 9 10 11 12 13
$ ld --verbose | grep SEARCH_DIR | sed "s/\;\ /\n/g" SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu") SEARCH_DIR("=/lib/x86_64-linux-gnu") SEARCH_DIR("=/usr/lib/x86_64-linux-gnu") SEARCH_DIR("=/usr/lib/x86_64-linux-gnu64") SEARCH_DIR("=/usr/local/lib64") SEARCH_DIR("=/lib64") SEARCH_DIR("=/usr/lib64") SEARCH_DIR("=/usr/local/lib") SEARCH_DIR("=/lib") SEARCH_DIR("=/usr/lib") SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64") SEARCH_DIR("=/usr/x86_64-linux-gnu/lib");
Так же есть возможность задавать дополнительные директории с библиотеками с помощью переменной окружения LD_LIBRARY_PATH. С помощью утилиты ldd посмотрим от каких библиотек зависит наша программа.
1 2 3 4 5
$ ldd a.out.2 linux-vdso.so.1 (0x00007ffffe4e7000) libsomecode.so => not found libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcf3b680000) /lib64/ld-linux-x86-64.so.2 (0x00007fcf3b897000)
Добавим в LD_LIBRARY_PATH текущую директорию.
$ export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$PWD"
Видим, что наша библиотека подгрузилась.
1 2 3 4 5
ldd a.out.2 linux-vdso.so.1 (0x00007fffe2115000) libsomecode.so (0x00007f4848a6c000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4848860000) /lib64/ld-linux-x86-64.so.2 (0x00007f4848a76000)
И теперь программа запускается и работает.
1 2 3 4 5
$ ./a.out.2 "hello my little pony" > hello > my > little > pony
Если сравнить два исполняемых файла, то видим, что программа, которая использует динамическую библиотеку имеет меньший размер.
1 2 3 4 5 6 7 8 9 10
$ ls -l total 92 -rwxr-xr-x 1 ainr ainr 19816 Aug 9 21:22 a.out.1 -rwxr-xr-x 1 ainr ainr 17864 Aug 9 21:25 a.out.2 -rwxr-xr-x 1 ainr ainr 18808 Aug 9 21:24 libsomecode.so -rw-r--r-- 1 ainr ainr 123 Aug 9 21:20 main.c -rw-r--r-- 1 ainr ainr 3576 Aug 9 21:23 main.o -rw-r--r-- 1 ainr ainr 213 Aug 9 21:21 somecode.c -rw-r--r-- 1 ainr ainr 72 Aug 9 21:20 somecode.h -rw-r--r-- 1 ainr ainr 6224 Aug 9 21:23 somecode.o
Это произошло как раз за счет того, что реализация нашей функции теперь лежит за пределами нашего исполняемого файла. С помощью утилиты objdump можем глянуть внутрь бинарных файлов. И увидим, что во втором бинарном файле реализация функции отсутствует, но присутствует в библиотеке.
1 2 3 4 5 6
$ objdump -t a.out.1 | grep print_split 000000000000119c g F .text 0000000000000061 print_split $ objdump -t a.out.2 | grep print_split 0000000000000000 F *UND* 0000000000000000 print_split $ objdump -t libsomecode.so | grep print_split 0000000000001139 g F .text 0000000000000061 print_split
Разница в нашем случае может и маленькая, но в масштабах десятков и сотен файлов разница будет значительной.
Так что же мы сделали?
У нас есть исходные файлы, мы скомпилировали их, получив из них динамическую библиотеку. Далее эту библиотеку можем использовать повторно в других проектах.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
-------------------------------- --------------------------------- | Заголовочный файл | | Реализация | | (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 и под мак, будут немного другие компиляторы, но идея одна.
Библиотеки
Библиотеки позволяют использовать разработанный ранее программный код в различных программах. Таким образом, программист может не разрабатывать часть кода для своей программы, а воспользоваться тем, что входит в состав библиотек.
В языке программирования C код библиотек представляет собой функции, размещенные в файлах, которые скомпилированы в объектные файлы, а те, в свою очередь, объединены в библиотеки. В одной библиотеке объединяются функции, решающие определенный тип задач. Например, существует библиотека математических функций.
У каждой библиотеки должен быть свой заголовочный файл, в котором должны быть описаны прототипы (объявления) всех функций, содержащихся в этой библиотеке. С помощью заголовочных файлов вы «сообщаете» вашему программному коду, какие библиотечные функции есть и как их использовать.
При компиляции программы библиотеки подключаются линковщиком, который вызывается gcc. Если программе требуются только стандартные библиотеки, то дополнительных параметров линковщику передавать не надо (есть исключения). Он «знает», где стандартные библиотеки находятся, и подключит их автоматически. Во всех остальных случаях при компиляции программы требуется указать имя библиотеки и ее местоположение.
Библиотеки бывают двух видов — статические и динамические. Код первых при компиляции полностью входит в состав исполняемого файла, что делает программу легко переносимой. Код динамических библиотек не входит в исполняемый файл, последний содержит лишь ссылку на библиотеку. Если динамическая библиотека будет удалена или перемещена в другое место, то программа работать не будет. С другой стороны, использование динамических библиотек позволяет сократить размер исполняемого файла. Также если в памяти находится две программы, использующие одну и туже динамическую библиотеку, то последняя будет загружена в память лишь единожды.
Далее будет описан пример, в котором создается библиотека, после чего используется при создании программы.
Пример создания библиотеки
Допустим, мы хотим создать код, который в дальнейшем планируем использовать в нескольких проектах. Следовательно, нам требуется создать библиотеку. Исходный код для библиотеки было решено разместить в двух файлах исходного кода.
Также на данный момент у нас есть план первого проекта, использующего эту библиотеку. Сам проект также будет включать два файла.
В итоге, когда все будет сделано, схема каталогов и файлов будет выглядеть так:
Пусть каталоги library и project находятся в одном общем каталоге, например, домашнем каталоге пользователя. Каталог library содержит каталог source с файлами исходных кодов библиотеки. Также в library будут находиться заголовочный файл (содержащий описания функций библиотеки), статическая (libmy1.a) и динамическая (libmy2.so) библиотеки. Каталог project будет содержать файлы исходных кодов проекта и заголовочный файл с описанием функций проекта. Также после компиляции с подключением библиотеки здесь будет располагаться исполняемый файл проекта.
В операционных системах GNU/Linux имена файлов библиотек должны иметь префикс «lib», статические библиотеки — расширение *.a, динамические — *.so.
Для компиляции проекта достаточно иметь только одну библиотеку: статическую или динамическую. В образовательных целях мы получим обе и сначала скомпилируем проект со статической библиотекой, потом — с динамической. Статическая и динамическая «разновидности» одной библиотеки по-идее должны называться одинаково (различаются только расширения). Поскольку у нас обе библиотеки будут находиться в одном каталоге, то чтобы быть уверенными, что при компиляции проекта мы используем ту, которую хотим, их названия различны (libmy1 и libmy2).
Исходный код библиотеки
void rect(char sign, int w, int h) int i, j; for (i=0; i w; i++) putchar(sign); putchar('\n'); for (i=0; i h-2; i++) { for (j=0; j w; j++) { if (j == 0 putchar('\n'); } for (i=0; i w; i++) putchar(sign); putchar('\n'); } void diagonals(char sign, int w) putchar('\n'); } }
В файле figure.c содержатся две функции — rect() и diagonals() . Первая принимает в качестве аргументов символ и два числа и «рисует» на экране с помощью указанного символа прямоугольник заданной ширины и высоты. Вторая функция выводит на экране две диагонали квадрата («рисует» крестик).
void text(char *ch) { while (*ch++ != '\0') putchar('*'); putchar('\n'); }
В файле text.c определена единственная функция, принимающая указатель на символ строки. Функция выводит на экране звездочки в количестве, соответствующем длине указанной строки.
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). Наличие такого кода позволяет библиотеке подключаться к программе, когда последняя загружается в память. Это связано с тем, что библиотека и программа не являются единой программой, а значит как угодно могут располагаться в памяти относительно друг друга. Компиляция объектных файлов для динамической библиотеки должна выполняться с опцией -fPIC компилятора gcc:
gcc -c -fPIC source/*.c
В отличие от статической библиотеки динамическую создают при помощи gcc указав опцию -shared:
gcc -shared -o libmy2.so *.o
Использованные объектные файлы можно удалить:
rm *.o
В итоге содержимое каталога library:
libmy1.a libmy2.so mylib.h source
Использование библиотеки в программе
Исходный код программы
Теперь в каталоге project (который у нас находится на одном уровне файловой иерархии с library) создадим файлы проекта, который будет использовать созданную библиотеку. Поскольку сама программа будет состоять не из одного файла, то придется здесь также создать заголовочный файл.
#include #include "../library/mylib.h" void data (void) { char strs[3][30]; char *prompts[3] = { "Ваше имя: ", "Местонахождение: ", "Пунк прибытия: "}; int i; for (i=0; i3; i++) { printf("%s", prompts[i]); gets(strs[i]); } diagonals('~', 7); for (i=0; i3; i++) { printf("%s", prompts[i]); text(strs[i]); } }
Функция data() запрашивает у пользователя данные, помещая их в массив strs. Далее вызывает библиотечную функцию diagonals() , которая выводит на экране «крестик». После этого на каждой итерации цикла вызывается библиотечная функция text() , которой передается очередной элемент массива; функция text() выводит на экране звездочки в количестве равному длине переданной через указатель строки.
Обратите внимание на то, как подключается заголовочный файл библиотеки: через относительный адрес. Две точки обозначают переход в каталог на уровень выше, т.е. родительский по отношению к project, после чего путь продолжается во вложенный в родительский каталог library. Можно было бы указать абсолютный путь, например, «/home/pl/c/les22/library/mylib.h». Однако при перемещении каталогов библиотеки и программы на другой компьютер или в другой каталог адрес был бы уже не верным. В случае с относительным адресом требуется лишь сохранять расположение каталогов project и library относительно друг друга.
#include #include "../library/mylib.h" #include "project.h" int 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. Их надо скомпилировать в исполняемый файл project, объединив со статической библиотекой libmy1.a. Делается это с помощью такой команды:
gcc -o project *.o -L../library -lmy1
Начало команды должно быть понятно: опция -o указывает на то, что компилируется исполняемый файл project из объектных файлов.
Помимо объектных файлов проекта в компиляции участвует и библиотека. Об этом свидетельствует вторая часть команды: -L../library -lmy1. Здесь опция -L указывает на адрес каталога, где находится библиотека, он и следует сразу за ней. После опции -l записывается имя библиотеки, при этом префикс lib и суффикс (неважно .a или .so) усекаются. Обратите внимание, что после данных опций пробел не ставится.
Опцию -L можно не указывать, если библиотека располагается в стандартных для данной системы каталогах для библиотек. Например, в GNU/Linux это /lib/, /urs/lib/ и др.
Запустив исполняемый файл project и выполнив программу, мы увидим на экране примерно следующее:
Посмотрим размер файла project:
pl@desk:~/c/project$ ls -l project -rwxr-xr-x 1 pl pl 8648 ноя 19 07:46 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/pl/c/library
, т.е. указать для линковщика абсолютный адрес, то программа в данной системе будет запускаться из любого каталога.
Размер исполняемого файла проекта, связанного с динамической библиотекой, получился равным 8544 байта. Это немного меньше, чем при компиляции проекта со статической библиотекой. Если посмотреть на размеры библиотек:
pl@desk:~/c/library$ ls -l libmy* -rw-r--r-- 1 pl pl 3712 ноя 19 07:35 libmy1.a -rwxr-xr-x 1 pl pl 7896 ноя 19 07:36 libmy2.so
, то видно, что динамическая больше статической, хотя исполняемый файл проекта со статической библиотекой больше. Это доказывает, что в исполняемом файле, связанном с динамической библиотекой, присутствует лишь ссылка на нее.
Курс с решением части задач:
pdf-версия