Как создать библиотеку c
Для создания статических библиотек существует специальная простая программа называемая ar (сокр. от archiver — архиватор). Она используется для создания, модификации и просмотра объектных файлов в статических библиотеках, которые в действительности представляют из себя простые архивы.
Давайте вернемся к проекту из шага «Шаг 3 — Компиляция нескольких файлов» и создадим из файлов f1.c и f2.c отдельную библиотеку. Для начала компилируем эти файлы:
dron:~# gcc -c f1.c f2.c
В результате получим, как обычно, два файла — f1.o и f2.o. Для того, чтобы создать библиотеку из объектых файлов надо вызвать программу ar со следующими параметрами:
ar rc libимя_библиотеки.a [список_*.o_файлов]
Допустим наша библиотека будет называться fs, тогда команда запишется в виде:
dron:~# ar rc libfs.a f1.o f2.o
В результате получим файл libfs.a, в котором будут лежать копии объектых файлов f1.o и f2.o. Если файл библиотеки уже существует, то архиватор будет анализировать содержимое архива, он добавит новые объектные файлы и заменит старые обновленными версиями. Опция c заставляет создавать (от create) библиотеку, если ее нет, а опция r (от replace) заменяет старые объектные файлы новыми версиями.
Пока у нас есть лишь архивный файл libfs.a. Чтобы из него сделать полноценную библиотеку объектных файлов надо добавить к этому архиву индекс символов, т.е. список вложенных в библиотеку функций и переменных, чтобы линковка происходила быстрее. Далается это командой:
ranlib libимя_библиотеки.a
Программа ranlib добавит индекс к архиву и получится полноценная статическая библиотека объектных файлов. Стоит отметить, что на некоторых системах программа ar автоматически создает индекс, и использование ranlib не имеет никакого эффекта. Но тут надо быть осторожным при атоматической компиляции библиотеки с помощью файлов makefile, если вы не будете использовать утилиту ranlib, то возможно на каких-то системах библиотеки будут создаваться не верно и потеряется независимость от платформы. Так что возьмем за правило тот факт, что утилиту ranlib надо запускать в любом случае, даже если он нее нет никакого эффекта.
Для компиляции нашего основного файла main.c надо сообщить компилятору, что надо использовать библиотеки. Чтобы компилятор знал где искать библиотеки ему надо сообщить каталог, в котором они содержатся и список этих билиотек. Каталог с библиотеками указывается ключом -L, в нашем случае библиотека находится в текущем каталоге, значит путь до нее будет в виде точки (-L.). Используемые библиотеки перечисляются через ключ -l, после которого указывается название библиотеки без префикса lib и окончания .a. В нашем случае этот ключ будет выглядеть, как -lfs. Теперь все одной командой:
dron:~# gcc -c main.c dron:~# gcc main.o -L. -lfs -o rezult
Или можно чуть короче:
dron:~# gcc main.c -L. -lfs -o rezult
Заметьте, что компилятору нужны библиотеки на этапе создания конечного файла, т.е. линковки. В первом случае процесс компиляции совершается первой командой, а сборка файла второй командой. Если же мы попытаемся подсунуть библиотеку на этапе компиляции, то получим вежливый ответ:
dron:~# gcc -c main.c -L. -lfs gcc: -lfs: linker input file unused since linking not done
Что означает, что файлы библиотек не нужны, до процесса линковки. Данная команда создаст лишь файл main.o, который в итоге потом придется собирать отдельно.
Создание статической библиотеки на С++ для работы с большими числами

Я всегда слышал, что с библиотеками в С++ что-то не так, как и с ограничением максимального целочисленного значения, да и вообще то, что язык сложный и непонятный. Что же, сегодня, мы начнём писать собственную библиотеку больших чисел, полностью своими руками c 0, и узнаем, так ли страшен С++, как его малюют?
Если вы не разбираетесь в С++, не переживайте, эта статья имеет нулевой порог вхождения. Мы начнём с лёгкого, но вы даже не заметите, как начнёте разбираться в более сложных и непонятных, на первый взгляд, вещах. Главное, писать код логично. Думаю, данная статья будет интересна не только начинающим, ведь я постарался затронуть достаточно много тем. (для старожилов: моя цель не сделать оптимизирование или быстрее, а показать, что С++ не такой уж и сложный язык программирования. И да, я знаю, что существуют другие библиотеки, которые делают это быстрее и лучше. И да, было бы круче, если бы мы использовали булевую алгебру. И да, С++ про вечную оптимизацию, но это статья не про это. Спасибо.)
За сегодня мы узнаем, что такое: Перегрузка функций/конструкторов, прототипы функций, обработка исключений, пространство имён, псевдонимы типов, заголовок.h, как пользоваться отладчиком и как писать продвинутые/красивые комментарии. Пристёгивайтесь, будет безумно интересно.
▍ Предисловие и планы
С++ строготипизированный язык программирования, где максимально возможное значение целочисленной переменной, является максимальное значение unsigned long long int (где-то 18 446 744 073 709 551 615). Этого бывает недостаточно, поэтому я решил разработать собственную библиотеку больших чисел (BigNumLib). Единственное ограничение размерности BigNumLib переменной – это количество цифр, из которого будет состоять число, то есть, максимально в число поместится 4 294 967 295 цифр.
▍ Начало разработки
Итак, начинаем разработку! Для начала нам необходимо продумать логику и возможности нашего собственного типа данных. Как мы создадим свой тип данных? В ЯП С++ нельзя расширить стандартные типы (int, double и т.п.), поэтому, единственный возможный вариант, который у нас остался, это работа через struct и class.
Чем отличаются Struct и Class?
Ответ: единственное различие между ними, так это то, что в struct модификаторы доступа по умолчанию public, а в class — private. Также отличается и наследование по умолчанию.
Итак, откроем Visual Studio с пустым проектом. Создадим папки (если они не созданы): “Файлы заголовков” с файлом BigNumLib.h и “Исходные файлы” с файлами Main.cpp, BigNumLib.cpp. У кого трудности на данном этапе, ничего страшного, ниже представлены фото и gif.
Как создать и настроить проект


В файле заголовка (.h), можно заметить строчку #pragma once. Что это?
Ответ: В языках программирования С и C++ #pragma once — нестандартная, но широко распространённая препроцессорная директива, разработанная для контроля за тем, чтобы конкретный исходный файл при компиляции подключался строго один раз.
Здесь, в этом файле заголовка, мы будем создавать прототипы функций (функции без тела) и вообще описывать весь класс, например, какие дополнительные библиотеки будут подключены, для исправной работы нашей, или какие функции будут доступны пользователю, а какие нет (модификаторы доступа public, private). Зачем мы вообще создали данный заголовок? Всё просто, чтобы подключить внешний код, необходимо использовать именно заголовок.
▍ Создание bignum класса
Итак, для начала нам необходимо создать класс и его поля:
class bignum
Ловим ошибку, что не подключили библиотеку и подключаем:
#include
Итак, что мы написали?
_value = здесь будет храниться наше число в виде строки
_size = из скольких цифр состоит наше число (размер). size_t это псевдоним, то есть, то же самое что и unsigned int (положительные целые числа)
_isNegative = является ли отрицательным числом. (true или false)
bignum() = конструктор класса. Он вызывается при создании экземпляра класса.
private: – поле, где доступ к данным имеет лишь класс. Приватные переменные, как правило, пишутся через ‘_’.
Так отлично, теперь откроем BigNumLib.cpp и напишем там такой код:
#include "bignum.h" bignum::bignum()
Здесь мы подключили наш заголовок и описали конструктор класса, где доступ к конструктору мы получаем через пространство имён ( bignum:: )
Теперь мы можем открыть наш основной файл ( Main.cpp ) и проверить работу библиотеки:
#include "BigNumLib.h" int main()
// Важное замечание, локальные заголовки, которые находятся в одном решении, подключаются с помощью кавычек.
Что же, теперь ставим точку остановы на return 0 , и смотрим нашу переменную.
Отлично, всё работает! По поводу отладчика, это безумно удобный интерфейс. Как сказал один мудрый человек, если программист не умеет пользоваться отладчиком, то этот человек не программист. Краткий экскурс по данному чуду: f5 (запуск отладки), shift+f5 (остановка), ctrl+shift+f5 (перезапуск), f10 (шаг с заходом в функцию), f11 (шаг с обходом функции), shift+f11 (шаг с выходом из функции), f5 (во время отладки, перейти к следующей точке остановы).
▍ Геттеры
Так, теперь создадим функцию геттер, чтобы иметь возможность читать поле нашего класса. Для этого объявим эту функцию в BigNumLib.h (файл заголовок) в поле public :
//@return string std::string getValue();
Мы написали комментарии в стиле DOC++. Этот тип комментариев понимает Visual Studio и красиво отображает нам. (ключевые слова пишутся через ‘@’: @return, @param )

Теперь необходимо прописать логику данных функций в BigNumLib.cpp .
std::string bignum::getValue() < std::string _value = this->_value; if (this->_isNegative) _value.insert(0, "-"); return _value; >
Чтобы обратиться к внутренним полям класса, мы используем указатель this , таким образом, пользователь имеет доступ к переменной _value только на чтение.
В функции getValue() мы создаём локальную переменную _value и заполняем её данными лежащими в поле класса. Если поле _isNegative имеет значение true (число отрицательно), то мы вставляем в начало строки ‘-’. (insert(позиция, знак))
Отлично, теперь проверим наш код в действии!
#include "BigNumLib.h" #include int main() < bignum a; std::cout
▍ Перегрузка конструктора (приём int)
Далее нам необходимо создать перегрузку конструктора класса, который на вход принимает long long int. Для этого объявим:
BigNumLib.h
public: bignum(long long int other_value);
BigNumLib.cpp
bignum::bignum(long long other_value)
Перегрузка конструктора \ функции \ оператора – это когда они имеют одно и то же имя, но принимают разные параметры. Та или иная функция вызывается в зависимости от принимаемых ею аргументов.
Здесь, мы впервые использовали тернарный оператор. Сокращённое написание конструкции if, else . Всё предельно просто, если на вход поступает отрицательное число, то поле _isNegative становится true . После чего, число переводится в строку и если число отрицательное, то удаляется первый символ из строки (‘-’).
Сейчас мы можем протестировать это и присвоить число. Попробуем присвоить положительное и отрицательное число, посмотрим, правильно ли работает наша программа:

Отлично, теперь попробуем ввести огромное число:

Как мы можем видеть, Visual Studio запрещает нам присваивать такое огромное число, потому что оно выходит за рамки long long int (превысив значение самого большого стандартного типа). Как мы будем обходить данный запрет? С помощью строки, ведь она, практически безгранична.
▍ Перегрузка конструктора (приём string, char*)
Создадим конструктор и пару функций в файле заголовка.
public: bignum(const char* other_value); bignum(std::string other_value); private: void parsStringToBigNumParams();
BigNumLib.cpp
bignum::bignum(const char* other_value) < _value = other_value; parsStringToBigNumParams(); >bignum::bignum(std::string other_value) < _value = other_value; parsStringToBigNumParams(); >void bignum::parsStringToBigNumParams() < if (_value[0] == '-') < _value = _value.erase(0, 1); _isNegative = true; >else _isNegative = false; if (_value.find_first_not_of("0123456789") != std::string::npos) throw std::runtime_error(_value + " it's not a number!"); _size = _value.size(); >
Мы написали 2 конструктора, где один из них принимает string, а другой массив char. Зачем? Потому что в случае, когда после присвоения сразу записывается значение, то будет массив char. А если создать string переменную и присвоить уже её, то активируется другой конструктор (со string параметром).

По поводу функции parsStringToBigNumParams() . Данная функция превращает строку, в набор параметров нашего класса. В начале она проверяет, стоит ли ‘-’, на первой позиции в _value , если да, убрать знак из строки и присвоить параметру _isNegative = true . После чего идёт проверка, если в _value найдено, что какой-то элемент не совпадает с цифирным набором (npos — не найдено совпадений), то выкинуть исключение. И дальше присвоить размер.
Что делает исключение? Полностью останавливает работу программы, если его не обработать. Давайте же опробуем работу наших конструкторов и обработаем исключение:

Как работает обработка исключений? В блоке try пишется небезопасный код, если в нём происходит ошибка, мы тут же попадаем в блок catch . Из-за того, что мы добавили описание в нашу самодельную ошибку, мы можем увидеть, какое значение, смогло сломать нашу библиотеку.
▍ Заключение
Что же, я чувствую, что выдал достаточно много инфы и, если продолжить, у новичков она может превратиться в кашу. За сегодня мы прошли очень много интересных тем и познакомились с некоторыми особенностями языка С++, но впереди ещё больше крутой информации, такая как перегрузка операторов, указатели, resize string и собственная логика в математических операциях. Если вам заходит такой формат обучения/разработки реального проекта, дайте знать, буду пилить 2 часть в таком же формате, ну, если вы вообще ждёте 2 часть 🙂

- c++
- c++ библиотеки
- visual c++
- visual studio
- создание библиотек
- написание библиотек
- большие числа
- большие данные
- ruvds_статьи
- Блог компании RUVDS.com
- C++
- Visual Studio
Пошаговое руководство. Создание и использование собственной библиотеки динамических ссылок (C++)
В этом пошаговом руководстве показано, как с помощью интегрированной среды разработки (IDE) Visual Studio создать собственную библиотеку динамической компоновки (DLL), написанную на Microsoft C++ (MSVC). Далее в нем показано, как использовать библиотеку DLL из другого приложения C++. Библиотеки DLL (также называемые общими библиотеками в операционных системах на основе UNIX) являются одним из наиболее полезных компонентов Windows. Их можно использовать для обмена кодом и ресурсами, а также для уменьшения размера своих приложений. Библиотеки DLL могут упростить обслуживание и расширение приложений.
В этом пошаговом руководстве вы создадите библиотеку DLL, которая реализует некоторые математические функции. Затем вы создадите консольное приложение, использующее функции из библиотеки DLL. Попутно вы познакомитесь с некоторыми методами программирования и соглашениями, используемыми в библиотеках DLL для Windows.
В этом пошаговом руководстве рассматриваются следующие задачи:
- Создание проекта библиотеки DLL в Visual Studio.
- Добавление экспортированных функций и переменных в библиотеку DLL.
- Создание проекта "Консольное приложение" в Visual Studio.
- Использование в консольном приложении функций и переменных, импортированных из библиотеки DLL.
- Запустите готовое приложение.
Как и в случае со статически связанной библиотекой, библиотека DLL экспортирует переменные, функции и ресурсы по имени. Клиентское приложение импортирует имена для использования этих переменных, функций и ресурсов. В отличие от статически компонуемой библиотеки, Windows соединяет импорт в вашем приложении с экспортом в библиотеку DLL во время загрузки или выполнения, а не во время компоновки. Для выполнения этих подключений Windows требуются дополнительные сведения, которые не являются частью стандартной модели компиляции C++. Чтобы предоставить эти дополнительные сведения, компилятор MSVC реализует некоторые специальные расширения Майкрософт для C++. Мы рассмотрим эти расширения далее.
В этом пошаговом руководстве создаются два решения Visual Studio. Первое решение создает библиотеку DLL, а второе — клиентское приложение. Библиотека DLL использует соглашение о вызовах языка C. Ее можно вызывать из приложений, написанных на других языках программирования, при условии, что платформа, соглашения о вызовах и соглашения о связывании совпадают. Клиентское приложение использует неявную компоновку, в рамках которой Windows связывает приложение с библиотекой DLL во время загрузки. Эта компоновка позволяет приложению вызывать функции, предоставляемые библиотекой DLL, точно так же, как функции в библиотеке статической компоновки.
В этом пошаговом руководстве не рассматриваются некоторые общие ситуации. В нем не рассматривается использование библиотеки DLL для C++ другими языками. Он не показывает, как создавать DLL-библиотеки только для ресурсов или как использовать явную компоновку для загрузки библиотек DLL во время выполнения, а не во время загрузки. Уверяем вас, все это можно выполнять с помощью MSVC и Visual C++.
Несмотря на то что код библиотеки DLL написан на языке C++, мы использовали интерфейсы в стиле C для экспортированных функций. Существует две основные причины: во-первых, многие другие языки поддерживают импорт функций в стиле C. Клиентское приложение не должно быть записано на языке C++. Во-вторых, это позволяет избежать некоторых распространенных ошибок, связанных с экспортируемыми классами и функциями-членами. При экспорте классов легко диагностировать ошибки, так как все, что упоминалось в объявлении класса, должно иметь экземпляр, который также экспортирован. Это ограничение применяется к библиотекам DLL, но не к статическим библиотекам. Если классы имеют обычный стиль данных, вы не должны столкнуться с этой проблемой.
Ссылки на дополнительные сведения о DLL см. в статье Создание библиотек DLL на C/C++ в Visual Studio. Дополнительные сведения о явной и неявной компоновке см. в разделе Определение подходящего метода связывания. Дополнительные сведения о создании библиотек DLL C++ для использования с языками, в которых применяются соглашения о компоновках языка C, см. в статье Экспорт функций на языке C++ для использования в исполняемых модулях, исходный код которых написан на языке C. Дополнительные сведения о том, как создавать библиотеки DLL для использования с языками .NET, см. в статье Вызов функций библиотек DLL из приложений Visual Basic.
Необходимые компоненты
- Компьютер под управлением Microsoft Windows 7 или более поздних версий. Мы рекомендуем использовать последнюю версию Windows для более удобной разработки.

- копия Visual Studio. Сведения о скачивании и установке Visual Studio см. в этой статье. Когда вы запускаете установщик, убедитесь, что установлена рабочая нагрузка Разработка классических приложений на C++. Не беспокойтесь, если вы не установили эту рабочую нагрузку при установке Visual Studio. Вы можете снова запустить установщик и установить ее сейчас.
- копия Visual Studio. Сведения о скачивании и установке Visual Studio 2015 см. в разделе Установка Visual Studio 2015. Для установки компилятора и средств используйте выборочную установку C++, так как они не установлены по умолчанию.
- Базовые значения об использовании интегрированной среды разработки Visual Studio. Если вы уже использовали классические приложения для Windows, вы, вероятно, справитесь. Общие сведения см. в обзоре возможностей интегрированной среды разработки Visual Studio.
- Основные навыки владения языком C++. Не волнуйтесь, мы не будем делать ничего сложного.
В данном пошаговом руководстве подразумевается, что вы используете Visual Studio 2017 версии 15.9 или более поздней. В некоторых более ранних версиях Visual Studio 2017 имелись дефекты в шаблонах кода или использовались разные диалоговые окна пользовательского интерфейса. Чтобы избежать проблем, используйте Visual Studio Installer для обновления Visual Studio 2017 до версии 15.9 или более поздней.
Создание проекта библиотеки DLL
Этот набор задач позволяет создать проект для библиотеки DLL, добавить код и выполнить его сборку. Для начала запустите IDE Visual Studio и выполните вход, если это необходимо. Инструкции немного отличаются в зависимости от используемой версии Visual Studio. Убедитесь, что в элементе управления в верхнем левом углу этой страницы выбрана правильная версия.
Создание проекта библиотеки DLL в Visual Studio 2019

- В строке меню выберите Файл>Создать>Проект, чтобы открыть диалоговое окно Создание проекта.
- В верхней части диалогового окна для параметра Язык установите значение C++, для параметра Платформа — значение Windows, а для параметра Тип проекта — значение Библиотека.
- В отфильтрованном списке типов проектов щелкните Библиотека динамической компоновки (DLL), а затем нажмите кнопку Далее.
- На странице Настроить новый проект введите MathLibrary в поле Имя проекта. Примите заданные по умолчанию Расположение и Имя решения. Для параметра Решение задайте Создать новое решение. Снимите флажок Разместить решение и проект в одном каталоге, если он установлен.
- Нажмите кнопку Создать, чтобы создать проект.
После создания решения созданный проект вместе с исходными файлами отобразится в окне обозревателя решений в Visual Studio.

Создание проекта библиотеки DLL в Visual Studio 2017

- В строке меню последовательно выберите пункты Файл>Создать>Проект, чтобы открыть диалоговое окно Новый проект.
- На панели слева в диалоговом окне Новый проект выберите Установленные >Visual C++ >Классическое приложение для Windows. В центральной области выберите Библиотека динамической компоновки (DLL). В поле Имя введите MathLibrary. Примите заданные по умолчанию Расположение и Имя решения. Для параметра Решение задайте Создать новое решение. Установите флажок Создать каталог для решения, если он снят.
- Нажмите кнопку ОК, чтобы создать проект.
После создания решения созданный проект вместе с исходными файлами отобразится в окне обозревателя решений в Visual Studio.

Создание проекта библиотеки DLL в Visual Studio 2015 и более ранних версий
- В строке меню выберите Файл >Создать >Проект.
- В левой области диалогового окна Новый проект разверните узлы Установленные>Шаблоны и выберите Visual C++, а затем в центральной области щелкните Консольное приложение Win32. В поле Имя введите MathLibrary. Примите заданные по умолчанию Расположение и Имя решения. Для параметра Решение задайте Создать новое решение. Установите флажок Создать каталог для решения, если он снят.

- Нажмите кнопку ОК, чтобы закрыть диалоговое окно Новый проект, и запустите мастер приложений Win32.

- Нажмите кнопку Далее. На странице Параметры приложения в поле Тип приложения выберите пункт DLL.

- Нажмите кнопку Готово , чтобы создать проект.
Когда мастер завершит создание решения, вы сможете увидеть созданный проект вместе с исходными файлами в окне обозревателя решений в Visual Studio.

Пока эта библиотека DLL ничего не делает. Затем вы создадите файл заголовка для объявления функций, экспортируемых вашей библиотекой DLL, и добавите определения функций в библиотеку DLL, чтобы сделать ее более полезной.
Добавление файла заголовка в библиотеку DLL
- Чтобы создать файл заголовка для функций, последовательно щелкните Проект>Добавить новый элемент.
- В диалоговом окне Добавление нового элемента в левой области щелкните Visual C++. В центральной области выберите Заголовочный файл (.h). Укажите MathLibrary.h в качестве имени для файла заголовка.

- Нажмите кнопку Добавить, чтобы создать пустой файл заголовка, который отображается в новом окне редактора.

- Замените все содержимое этого файла заголовка следующим кодом:
// MathLibrary.h - Contains declarations of math functions #pragma once #ifdef MATHLIBRARY_EXPORTS #define MATHLIBRARY_API __declspec(dllexport) #else #define MATHLIBRARY_API __declspec(dllimport) #endif // The Fibonacci recurrence relation describes a sequence F // where F(n) is < n = 0, a // < n = 1, b // < n >1, F(n-2) + F(n-1) // for some initial integral values a and b. // If the sequence is initialized F(0) = 1, F(1) = 1, // then this relation produces the well-known Fibonacci // sequence: 1, 1, 2, 3, 5, 8, 13, 21, 34, . // Initialize a Fibonacci relation sequence // such that F(0) = a, F(1) = b. // This function must be called before any other function. extern "C" MATHLIBRARY_API void fibonacci_init( const unsigned long long a, const unsigned long long b); // Produce the next value in the sequence. // Returns true on success and updates current value and index; // false on overflow, leaves current value and index unchanged. extern "C" MATHLIBRARY_API bool fibonacci_next(); // Get the current value in the sequence. extern "C" MATHLIBRARY_API unsigned long long fibonacci_current(); // Get the position of the current value in the sequence. extern "C" MATHLIBRARY_API unsigned fibonacci_index();
Этот файл заголовка объявляет некоторые функции для создания обобщенной последовательности Фибоначчи, исходя из двух начальных значений. Вызов fibonacci_init(1, 1) создает знакомую последовательность чисел Фибоначчи.
Обратите внимание на операторы препроцессора в верхней части файла. Новый шаблон проекта библиотеки DLL добавляется _EXPORTS к определенным макросам препроцессора. В этом примере Visual Studio определяет MATHLIBRARY_EXPORTS , когда создается проект DLL MathLibrary.
MATHLIBRARY_EXPORTS При определении MATHLIBRARY_API макроса макрос задает __declspec(dllexport) модификатор для объявлений функций. Этот модификатор предписывает компилятору и компоновщику экспортировать функцию или переменную из библиотеки DLL для использования другими приложениями. Если не определено, например, если MATHLIBRARY_EXPORTS файл заголовка включен клиентским приложением, MATHLIBRARY_API применяет __declspec(dllimport) модификатор к объявлениям. Этот модификатор оптимизирует импорт функции или переменной в приложении. Дополнительные сведения см. в статье dllexport, dllimport.
Добавление реализации в библиотеку DLL
- В обозревателе решений щелкните узел Файлы решения правой кнопкой мыши и выберите пункты Добавить >Новый элемент. Создайте новый CPP-файл с именем MathLibrary.cpp, аналогично добавлению нового файла заголовка на предыдущем шаге.
- В окне редактора выберите вкладку MathLibrary.cpp, если она уже открыта. Если нет, то в обозревателе решений дважды щелкните файл MathLibrary.cpp в папке Исходные файлы проекта MathLibrary.
- В редакторе замените содержимое файла MathLibrary.cpp следующим кодом:
// MathLibrary.cpp : Defines the exported functions for the DLL. #include "pch.h" // use stdafx.h in Visual Studio 2017 and earlier #include #include #include "MathLibrary.h" // DLL internal state variables: static unsigned long long previous_; // Previous value, if any static unsigned long long current_; // Current sequence value static unsigned index_; // Current seq. position // Initialize a Fibonacci relation sequence // such that F(0) = a, F(1) = b. // This function must be called before any other function. void fibonacci_init( const unsigned long long a, const unsigned long long b) < index_ = 0; current_ = a; previous_ = b; // see special case when initialized >// Produce the next value in the sequence. // Returns true on success, false on overflow. bool fibonacci_next() < // check to see if we'd overflow result or position if ((ULLONG_MAX - previous_ < current_) || (UINT_MAX == index_)) < return false; >// Special case when index == 0, just return b value if (index_ > 0) < // otherwise, calculate next sequence value previous_ += current_; >std::swap(current_, previous_); ++index_; return true; > // Get the current value in the sequence. unsigned long long fibonacci_current() < return current_; >// Get the current index position in the sequence. unsigned fibonacci_index()
- В окне редактора выберите вкладку MathLibrary.cpp, если она уже открыта. Если нет, то в обозревателе решений дважды щелкните файл MathLibrary.cpp в папке Исходные файлы проекта MathLibrary.
- В редакторе замените содержимое файла MathLibrary.cpp следующим кодом:
// MathLibrary.cpp : Defines the exported functions for the DLL. #include "stdafx.h" // use pch.h in Visual Studio 2019 and later #include #include #include "MathLibrary.h" // DLL internal state variables: static unsigned long long previous_; // Previous value, if any static unsigned long long current_; // Current sequence value static unsigned index_; // Current seq. position // Initialize a Fibonacci relation sequence // such that F(0) = a, F(1) = b. // This function must be called before any other function. void fibonacci_init( const unsigned long long a, const unsigned long long b) < index_ = 0; current_ = a; previous_ = b; // see special case when initialized >// Produce the next value in the sequence. // Returns true on success, false on overflow. bool fibonacci_next() < // check to see if we'd overflow result or position if ((ULLONG_MAX - previous_ < current_) || (UINT_MAX == index_)) < return false; >// Special case when index == 0, just return b value if (index_ > 0) < // otherwise, calculate next sequence value previous_ += current_; >std::swap(current_, previous_); ++index_; return true; > // Get the current value in the sequence. unsigned long long fibonacci_current() < return current_; >// Get the current index position in the sequence. unsigned fibonacci_index()
Чтобы убедиться, что все работает, скомпилируйте библиотеку динамической компоновки. Чтобы выполнить компиляцию, последовательно выберите Сборка>Собрать решение. Библиотека DLL и связанные выходные данные компилятора помещаются в папку с именем Debug непосредственно под папкой решения. При создании сборки выпуска выходные данные помещаются в папку с именем Release. Результат должен выглядеть следующим образом.
1>------ Build started: Project: MathLibrary, Configuration: Debug Win32 ------ 1>pch.cpp 1>dllmain.cpp 1>MathLibrary.cpp 1>Generating Code. 1> Creating library C:\Users\username\Source\Repos\MathLibrary\Debug\MathLibrary.lib and object C:\Users\username\Source\Repos\MathLibrary\Debug\MathLibrary.exp 1>MathLibrary.vcxproj -> C:\Users\username\Source\Repos\MathLibrary\Debug\MathLibrary.dll ========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
1>------ Build started: Project: MathLibrary, Configuration: Debug Win32 ------ 1>stdafx.cpp 1>dllmain.cpp 1>MathLibrary.cpp 1>Generating Code. 1> Creating library C:\Users\username\Source\Repos\MathLibrary\Debug\MathLibrary.lib and object C:\Users\username\Source\Repos\MathLibrary\Debug\MathLibrary.exp 1>MathLibrary.vcxproj -> C:\Users\username\Source\Repos\MathLibrary\Debug\MathLibrary.dll ========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
1>------ Build started: Project: MathLibrary, Configuration: Debug Win32 ------ 1>MathLibrary.cpp 1>dllmain.cpp 1>Generating Code. 1> Creating library C:\Users\username\Source\Repos\MathLibrary\Debug\MathLibrary.lib and object C:\Users\username\Source\Repos\MathLibrary\Debug\MathLibrary.exp 1>MathLibrary.vcxproj -> C:\Users\username\Source\Repos\MathLibrary\Debug\MathLibrary.dll 1>MathLibrary.vcxproj -> C:\Users\username\Source\Repos\MathLibrary\Debug\MathLibrary.pdb (Partial PDB) ========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
Поздравляем, вы создали библиотеку DLL с помощью Visual Studio! Далее вы создадите клиентское приложение, которое использует функции, экспортируемые из библиотеки DLL.
Создание клиентского приложения, которое использует библиотеку DLL
При создании библиотеки DLL подумайте о том, как клиентские приложения могут его использовать. Чтобы вызвать функции или получить доступ к данным, экспортированным библиотекой DLL, исходный код клиента должен иметь объявления, доступные во время компиляции. Во время компоновки компоновщику требуются сведения для разрешения вызовов функций или доступа к данным. Библиотека DLL предоставляет эти сведения в библиотеке импорта — файле, который содержит сведения о поиске функций и данных вместо фактического кода. Во время выполнения библиотека DLL должна быть доступна клиенту в месте, которое может найти операционная система.
Независимо от того, является ли он вашим или предоставлен сторонним разработчиком, вашему проекту клиентского приложения требуется несколько фрагментов информации для использования библиотеки DLL. Ему необходимо найти заголовки, в которых объявляются экспорты библиотеки DLL, библиотеки импорта для компоновщика и сама библиотека DLL. Одним из решений является копирование всех этих файлов в ваш клиентский проект. Для сторонних библиотек DLL, которые вряд ли изменятся во время разработки вашего клиента, этот метод может быть лучшим способом их использования. Однако, когда вы также создаете библиотеку DLL, лучше избегать дублирования. Если вы делаете локальную копию файлов библиотеки DLL, которые находятся в стадии разработки, вы можете случайно изменить файл заголовка только в одной копии или использовать устаревшую библиотеку.
Чтобы избежать рассинхронизации, мы рекомендуем вам установить путь включения в своем клиентском проекте, чтобы добавить файлы заголовков библиотеки DLL напрямую из проекта DLL. Кроме того, укажите путь к библиотеке в своем клиентском проекте, чтобы добавить библиотеки импорта DLL из проекта DLL. Наконец, скопируйте встроенную библиотеку DLL из проекта DLL в выходной каталог своей сборки клиента. Этот шаг позволяет вашему клиентскому приложению использовать тот же код библиотеки DLL, который вы создали.
Создание клиентского приложения в Visual Studio

- В строке меню выберите "Файл>нового проекта">, чтобы открыть диалоговое окно "Создать проект".
- В верхней части диалогового окна задайте для параметра Язык значение C++, для параметра Платформа значение Windows, а для Типа проекта — Консоль.
- В отфильтрованном списке типов проектов щелкните Консольное приложение, а затем нажмите кнопку Далее.
- На странице Настроить новый проект введите MathClient в поле Имя проекта. Примите заданные по умолчанию Расположение и Имя решения. Для параметра Решение задайте Создать новое решение. Снимите флажок Разместить решение и проект в одном каталоге, если он установлен.
- Нажмите кнопку Создать, чтобы создать клиентский проект.
Создается минимальный проект консольного приложения. Имя главного исходного файла будет совпадать с ранее введенным именем проекта. В этом примере используется имя MathClient.cpp. Вы можете создать проект, но он еще не использует вашу библиотеку DLL.
Создание клиентского приложения в Visual Studio 2017
Нажмите кнопку ОК, чтобы создать проект клиентского приложения.
Создается минимальный проект консольного приложения. Имя главного исходного файла будет совпадать с ранее введенным именем проекта. В этом примере используется имя MathClient.cpp. Вы можете создать проект, но он еще не использует вашу библиотеку DLL.
Создание клиентского приложения в Visual Studio 2015
Нажмите кнопку ОК, чтобы закрыть диалоговое окно Новый проект, и запустите мастер приложений Win32. На странице Обзор диалогового окна Мастер приложений Win32 нажмите кнопку Далее .
На странице Параметры приложения в поле Тип приложения выберите пункт Консольное приложение, если он еще не выбран.
Нажмите кнопку Готово , чтобы создать проект.
После завершения работы мастера создается минимальный проект консольного приложения. Имя главного исходного файла будет совпадать с ранее введенным именем проекта. В этом примере используется имя MathClient.cpp. Вы можете создать проект, но он еще не использует вашу библиотеку DLL.
Затем, чтобы вызвать функции MathLibrary в исходном коде, ваш проект должен содержать файл MathLibrary.h. Этот файл заголовка можно скопировать в проект клиентского приложения, а затем добавить его в проект как существующий элемент. Этот метод подходит для сторонних библиотек. Однако если вы работаете над кодом для библиотеки DLL и клиента одновременно, файлы заголовков могут выйти из синхронизации. Чтобы избежать этой проблемы, задайте в проекте путь "Дополнительные каталоги включения" , чтобы включить путь к исходному заголовку.
Добавление заголовка библиотеки DLL в путь включения
- Щелкните правой кнопкой мыши узел MathClient в обозревателе решений, чтобы открыть диалоговое окно Страницы свойств.
- В раскрывающемся списке Конфигурация выберите пункт Все конфигурации, если он еще не выбран.
- В области слева выберите пункт Свойства конфигурации >C/C++ >Общие.
- На панели свойств щелкните раскрывающийся элемент управления рядом с полем ввода параметра Дополнительные каталоги включаемых файлов, а затем щелкните Правка.

- Дважды щелкните в верхней панели диалогового окна Дополнительные каталоги включаемых файлов, чтобы включить элемент управления "Поле ввода". Или щелкните значок папки, чтобы создать новую запись.
- В элементе управления "Поле ввода" укажите путь к расположению файла заголовка MathLibrary.h. Чтобы перейти к нужной папке, можно выбрать элемент управления с многоточием (. ). Можно также ввести относительный путь от исходных файлов клиента к папке, содержащей файлы заголовков библиотеки DLL. Если вы следовали инструкциям по размещению клиентского проекта в отдельном решении, отличном от библиотеки DLL, относительный путь должен выглядеть следующим образом: ..\..\MathLibrary\MathLibrary Если библиотеки DLL и клиентские проекты находятся в одном решении, относительный путь может выглядеть следующим образом: ..\MathLibrary Если библиотеки DLL и клиентские проекты находятся в других папках, измените относительный путь для соответствия. Или используйте элемент управления "Многоточие" для поиска папки.

- После ввода пути к файлу заголовка в диалоговом окне Дополнительные каталоги включаемых файлов нажмите кнопку ОК. В диалоговом окне Страницы свойств нажмите кнопку OK, чтобы сохранить изменения.
Теперь можно добавить файл MathLibrary.h и использовать функции, которые он объявляет, в вашем клиентском приложении. Замените содержимое файла MathClient.cpp, используя следующий код:
// MathClient.cpp : Client app for MathLibrary DLL. // #include "pch.h" Uncomment for Visual Studio 2017 and earlier #include #include "MathLibrary.h" int main() < // Initialize a Fibonacci relation sequence. fibonacci_init(1, 1); // Write out the sequence values until overflow. do < std::cout while (fibonacci_next()); // Report count of values written before overflow. std::cout
Этот код может быть скомпилирован, но не скомпилирован. Если вы создаете клиентское приложение, в списке ошибок появится несколько ошибок LNK2019. Это связано с отсутствием некоторых сведений о проекте: вы еще не указали, что проект зависит от библиотеки MathLibrary.lib . И вы не указали компоновщику, как найти файл MathLibrary.lib.
Чтобы устранить эту проблему, можно скопировать файл библиотеки непосредственно в проект клиентского приложения. Компоновщик сможет найти и использовать его автоматически. Однако если и библиотека, и клиентское приложение находятся в стадии разработки, это может привести к изменениям в одной копии, которые не будут отображаться в другой. Чтобы избежать этой проблемы, можно задать свойство Дополнительные зависимости, чтобы сообщить системе сборки о том, что проект зависит от MathLibrary.lib. Также можно задать путь Дополнительные каталоги библиотек в проекте, включив в него путь к исходной библиотеке при компоновке.
Добавление библиотеки импорта DLL в проект
- Щелкните правой кнопкой мыши узел MathClient в обозревателе решений и выберите Свойства, чтобы открыть диалоговое окно Страницы свойств.
- В раскрывающемся списке Конфигурация выберите пункт Все конфигурации, если он еще не выбран. Это гарантирует, что любые изменения свойств применяются к сборкам отладки и выпуска.
- В области слева выберите пункт Свойства конфигурации >Компоновщик>Ввод. На панели свойств щелкните раскрывающийся элемент управления рядом с полем ввода параметра Дополнительные зависимости, а затем щелкните Правка.

- В диалоговом окне Дополнительные зависимости добавьте MathLibrary.lib в список в верхнем элементе управления "Поле ввода".

- Нажмите кнопку OK, чтобы вернуться в диалоговое окно Страницы свойств.
- В области слева выберите пункт Свойства конфигурации >Компоновщик>Общие. На панели свойств щелкните раскрывающийся элемент управления рядом с полем ввода параметра Дополнительные каталоги библиотек, а затем щелкните Правка.

- Дважды щелкните в верхней панели диалогового окна Дополнительные каталоги библиотек, чтобы включить элемент управления "Поле ввода". В элементе управления "Поле ввода" укажите путь к расположению файла MathLibrary.lib. По умолчанию он находится в папке с именем Debug непосредственно в папке решения DLL. При создании сборки выпуска файл помещается в папку с именем Release. Можно использовать макрос $(IntDir) , чтобы компоновщик мог найти библиотеку DLL независимо от типа создаваемой сборки. Если вы следовали инструкциям по размещению клиентского проекта в отдельном решении, отличном от проекта DLL, относительный путь должен выглядеть следующим образом: ..\..\MathLibrary\$(IntDir) Если библиотеки DLL и клиентские проекты находятся в других расположениях, измените относительный путь для соответствия.

- Как только вы ввели путь к файлу библиотеки, в диалоговом окне Дополнительные каталоги библиотек нажмите кнопку ОК, чтобы вернуться в диалоговое окно Страницы свойств. Нажмите ОК, чтобы сохранить изменения свойств.
Ваше клиентское приложение теперь можно компилировать и компоновать, но в нем по-прежнему нет всего необходимого для запуска. Когда операционная система загружает ваше приложение, оно ищет библиотеку DLL MathLibrary. Если она не может найти библиотеку DLL в определенных системных каталогах, в пути среды или локальном каталоге приложения, загрузка завершается сбоем. В зависимости от операционной системы вы увидите сообщение об ошибке следующего вида:

Чтобы избежать этой проблемы, можно скопировать библиотеку DLL в каталог, в котором находится исполняемый файл клиента, в процессе сборки. Можно добавить событие после сборки в ваш проект, чтобы добавить команду, которая копирует библиотеку DLL в выходной каталог вашей сборки. Указанная здесь команда копирует библиотеку DLL только в том случае, если она отсутствует или была изменена. Он использует макросы для копирования в расположения отладки или выпуска на основе конфигурации сборки.
Копирование библиотеки DLL в событие после сборки

- Щелкните правой кнопкой мыши узел MathClient в обозревателе решений и выберите Свойства, чтобы открыть диалоговое окно Страницы свойств.
- В раскрывающемся списке Конфигурация выберите пункт Все конфигурации, если он еще не выбран.
- В области слева выберите Свойства конфигурации >События сборки >Событие после сборки.
- В области свойств щелкните элемент управления "Поле ввода" в поле Командная строка. Если вы следовали инструкциям по размещению клиентского проекта в отдельном решении, отличном от проекта DLL, введите следующую команду: xcopy /y /d "..\..\MathLibrary\$(IntDir)MathLibrary.dll" "$(OutDir)" Если библиотеки DLL и клиентские проекты находятся в других каталогах, измените относительный путь к библиотеке DLL для соответствия.
- Нажмите кнопку OK, чтобы сохранить изменения в свойствах проекта.
Теперь в вашем клиентском приложении есть все, что нужно для сборки и запуска. Соберите приложение, щелкнув команду Сборка>Собрать решение в меню. Окно Вывод в Visual Studio должно иметь примерно следующий вид в зависимости от используемой версии Visual Studio:
1>------ Build started: Project: MathClient, Configuration: Debug Win32 ------ 1>MathClient.cpp 1>MathClient.vcxproj -> C:\Users\username\Source\Repos\MathClient\Debug\MathClient.exe 1>1 File(s) copied ========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
Поздравляем, вы создали приложение, которое вызывает функции в вашей библиотеке DLL. Теперь запустите свое приложение, чтобы увидеть, как оно работает. В строке меню щелкните Отладка>Начать без отладки. В Visual Studio открывается командное окно для запуска программы. Последняя часть выходных данных должна выглядеть так:

Для закрытия командного окна нажмите любую клавишу.
Теперь, когда вы создали библиотеку DLL и клиентское приложение, вы можете экспериментировать. Попробуйте задать точки останова в коде клиентского приложения и запустите приложение в отладчике. Посмотрите, что происходит, когда вы входите в вызов библиотеки. Добавьте другие функции в библиотеку или напишите другое клиентское приложение, которое использует вашу библиотеку DLL.
При развертывании приложения необходимо также развернуть используемые им библиотеки DLL. Самый простой способ сделать библиотеки DLL, которые вы создаете или добавляете из сторонних источников, доступными — поместить их в тот же каталог, что и ваше приложение. Это также называется локальным развертыванием приложений. Дополнительные сведения о развертывании см. в разделе Deployment in Visual C++.
Как создать библиотеку c
Как уже неоднократно упоминалось в предыдущей главе, библиотека - это набор скомпонованных особым образом объектных файлов. Библиотеки подключаются к основной программе во время линковки. По способу компоновки библиотеки подразделяют на архивы (статические библиотеки, static libraries) и совместно используемые (динамические библиотеки, shared libraries). В Linux, кроме того, есть механизмы динамической подгрузки библиотек. Суть динамической подгрузки состоит в том, что запущенная программа может по собственному усмотрению подключить к себе какую-либо библиотеку. Благодаря этой возможности создаются программы с подключаемыми плагинами, такие как XMMS. В этой главе мы не будем рассматривать динамическую подгрузку, а остановимся на классическом использовании статических и динамических библиотек.
С точки зрения модели КИС, библиотека - это сервер. Библиотеки несут в себе одну важную мысль: возможность использовать одни и те же механизмы в разных программах. В Linux библиотеки используются повсеместно, поскольку это очень удобный способ "не изобретать велосипеды". Даже ядро Linux в каком-то смысле представляет собой библиотеку механизмов, называемых системными вызовами.
Статическая библиотека - это просто архив объектных файлов, который подключается к программе во время линковки. Эффект такой же, как если бы вы подключали каждый из файлов отдельно.
В отличие от статических библиотек, код совместно используемых (динамических) библиотек не включается в бинарник. Вместо этого в бинарник включается только ссылка на библиотеку.
Рассмотрим преимущества и недостатки статических и совместно используемых библиотек. Статические библиотеки делают программу более автономной: программа, скомпонованная со статической библиотекой может запускаться на любом компьютере, не требуя наличия этой библиотеки (она уже "внутри" бинарника). Программа, скомпонованная с динамической библиотекой, требует наличия этой библиотеки на том компьютере, где она запускается, поскольку в бинарнике не код, а ссылка на код библиотеки. Не смотря на такую зависимость, динамические библиотеки обладают двумя существенными преимуществами. Во-первых, бинарник, скомпонованный с совместно используемой библиотекой меньше размером, чем такой же бинарник, с подключенной к нему статической библиотекой (статически скомпонованный бинарник). Во-вторых, любая модернизация динамической библиотеки, отражается на всех программах, использующих ее. Таким образом, если некоторую библиотеку foo используют 10 программ, то исправление какой-нибудь ошибки в foo или любое другое улучшение библиотеки автоматически улучшает все программы, которые используют эту библиотеку. Именно поэтому динамические библиотеки называют совместно используемыми. Чтобы применить изменения, внесенные в статическую библиотеку, нужно пересобрать все 10 программ.
В Linux статические библиотеки обычно имеют расширение .a (Archive), а совместно используемые библиотеки имеют расширение .so (Shared Object). Хранятся библиотеки, как правило, в каталогах /lib и /usr/lib. В случае иного расположения (относится только к совместно используемым библиотекам), приходится немного "подшаманить", чтобы программа запустилась.
3.2. Пример статической библиотеки
Теперь давайте создадим свою собственную библиотеку, располагающую двумя функциями: h_world() и g_world(), которые выводят на экран "Hello World" и "Goodbye World" соответственно. Начнем со статической библиотеки.
Начнем с интерфейса. Создадим файл world.h:
/* world.h */ void h_world (void); void g_world (void);
Здесь просто объявлены функции, которые будут использоваться.
Теперь надо реализовать серверы. Создадим файл h_world.c:
/* h_world.c */ #include #include "world.h" void h_world (void)
Теперь создадим файл g_world.c, содержащий реализацию функции g_world():
/* g_world.c */ #include #include "world.h" void g_world (void)
Можно было бы с таким же успехом уместить обе функции в одном файле (hello.c, например), однако для наглядности мы разнесли код на два файла.
Теперь создадим файл main.c. Это клиент, который будет пользоваться услугами сервера:
/* main.c */ #include "world.h" int main (void)
Теперь напишем сценарий для make. Для этого создаем Makefile:
# Makefile for World project binary: main.o libworld.a gcc -o binary main.o -L. -lworld main.o: main.c gcc -c main.c libworld.a: h_world.o g_world.o ar cr libworld.a h_world.o g_world.o h_world.o: h_world.c gcc -c h_world.c g_world.o: g_world.c gcc -c g_world.c clean: rm -f *.o *.a binary
Не забывайте ставить табуляции перед каждым правилом в целевых связках.
$ make gcc -c main.c gcc -c h_world.c gcc -c g_world.c ar cr libworld.a h_world.o g_world.o gcc -o binary main.o -L. -lworld $
Осталось только проверить, работает ли программа и разобраться, что же мы такое сделали:
$ ./binary Hello World Goodbye World $
Итак, в приведенном примере появились три новые вещи: опции -l и -L компилятора, а также команда ar. Начнем с последней. Как вы уже догадались, команда ar создает статическую библиотеку (архив). В нашем случае два объектных файла объединяются в один файл libworld.a. В Linux практически все библиотеки имеют префикс lib.
Как уже говорилось, компилятор gcc сам вызывает линковщик, когда это нужно. Опция -l, переданная компилятору, обрабатывается и посылается линковщику для того, чтобы тот подключил к бинарнику библиотеку. Как вы уже заметили, у имени библиотеки "обрублены" префикс и суффикс. Это делается для того, чтобы создать "видимое безразличие" между статическими и динамическими библиотеками. Но об этом речь пойдет в других главах книги. Сейчас важно знать лишь то, что и библиотека libfoo.so и библиотека libfoo.a подключаются к проекту опцией -lfoo. В нашем случае libworld.a "урезалось" до -lworld.
Опция -L указывает линковщику, где ему искать библиотеку. В случае, если библиотека располагается в каталоге /lib или /usr/lib, то вопрос отпадает сам собой и опция -L не требуется. В нашем случае библиотека находится в репозитории (в текущем каталоге). По умолчанию линковщик не просматривает текущий каталог в поиске библиотеки, поэтому опция -L. (точка означает текущий каталог) необходима.
3.3. Пример совместно используемой библиотеки
Для того, чтобы создать и использовать динамическую (совместно используемую) библиотеку, достаточно переделать в нашем проекте Makefile.
# Makefile for World project binary: main.o libworld.so gcc -o binary main.o -L. -lworld -Wl,-rpath,. main.o: main.c gcc -c main.c libworld.so: h_world.o g_world.o gcc -shared -o libworld.so h_world.o g_world.o h_world.o: h_world.c gcc -c -fPIC h_world.c g_world.o: g_world.c gcc -c -fPIC g_world.c clean: rm -f *.o *.so binary
Внешне ничего не изменилось: программа компилируется, запускается и выполняет те же самые действия, что и в предыдущем случае. Изменилась внутренняя суть, которая играет для программиста первоочередную роль. Рассмотрим все по порядку.
Правило для сборки binary теперь содержит пугающую опцию -Wl,-rpath,. Ничего страшного тут нет. Как уже неоднократно говорилось, компилятор gcc сам вызывает линковщик ld, когда это надо и передает ему нужные параметры сборки, избавляя нас от ненужной платформенно-зависимой волокиты. Но иногда мы все-таки должны вмешаться в этот процесс и передать линковщику "свою" опцию. Для этого используется опция компилятора -Wl,option,optargs. Расшифровываю: передать линковщику (-Wl) опцию option с аргументами optargs. В нашем случае мы передаем линковщику опцию -rpath с аргументом . (точка, текущий каталог). Возникает вопрос: что означает опция -rpath? Как уже говорилось, линковщик ищет библиотеки в определенных местах; обычно это каталоги /lib и /usr/lib, иногда /usr/local/lib. Опция -rpath просто добавляет к этому списку еще один каталог. В нашем случае это текущий каталог. Без указания опции -rpath, линковщик "молча" соберет программу, но при запуске нас будет ждать сюрприз: программа не запустится из-за отсутствия библиотеки. Попробуйте убрать опцию -Wl,-rpath,. из Makefile и пересоберите проект. При попытке запуска программа binary завершится с кодом возврата 127 (о кодах возврата будет рассказано в последующих главах). То же самое произойдет, если вызвать программу из другого каталога. Верните обратно -Wl,-rpath. пересоберите проект, поднимитесь на уровень выше командой cd .. и попробуйте запустить бинарник командой world/binary. Ничего не получится, поскольку в новом текущем каталоге библиотеки нет.
Есть один способ не передавать линковщику дополнительных опций при помощи -Wl - это использование переменной окружения LD_LIBRARY_PATH. В последующих главах мы будем подробно касаться темы окружения (environment). Сейчас лишь скажу, что у каждого пользователя есть так называемое окружение (environment) представляющее собой набор пар ПЕРЕМЕННАЯ=ЗНАЧЕНИЕ, используемых программами. Чтобы посмотреть окружение, достаточно набрать команду env. Чтобы добавить в окружение переменную, достаточно набрать export ПЕРЕМЕННАЯ=ЗНАЧЕНИЕ, а чтобы удалить переменную из окружения, надо набрать export -n ПЕРЕМЕННАЯ. Будьте внимательны: export - это внутреннаяя команда оболочки BASH; в других оболочках (csh, ksh, . ) используются другие команды для работы с окружением. Переменная окружения LD_LIBRARY_PATH содержит список дополнительных "мест", разделенных двоеточиеями, где линковщих должен искать библиотеку.
Не смотря на наличие двух механизмов передачи информации о нестандартном расположении библиотек, лучше помещать библиотеки в конечных проектах в /lib и в /usr/lib. Допускается расположение библиотек в подкаталоги /usr/lib и в /usr/local/lib (с указанем -Wl,-rpath). Но заставлять конечного пользователя устанавливать LD_LIBRARY_PATH почти всегда является плохим стилем программирования.
Следующая немаловажная деталь - это процесс создания самой библиотеки. Статические библиотеки создаются при помощи архиватора ar, а совместно используемые - при помощи gcc с опцией -shared. В данном случае gcc опять же вызывает линковщик, но не для сборки бинарника, а для создания динамической библиотеки.
Последнее отличие - опциии -fPIC (-fpic) при компиляции h_world.c и g_world.c. Эта опция сообщает компилятору, что объектные файлы, полученные в результате компиляции должны содержать позиционно-независимый код (PIC - Position Independent Code), который используется в динамических библиотеках. В таком коде используются не фиксированные позиции (адреса), а плавающие, благодаря чему код из библиотеки имеет возможность подключаться к программе в момент запуска.