Когда использовать динамические или статические библиотеки

При создании библиотеки классов на C++ вы можете выбирать между динамическими (.dll, .so) и статическими (.lib, .a) библиотеками. В чем разница между ними и какие из них целесообразно использовать?

Следует отметить, что есть также что-то, что называется "Импортировать библиотеку", проверка stackoverflow.com/questions/3573475/…

Wakan Tanka 09.09.2016 01:47
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
449
1
249 893
18
Перейти к ответу Данный вопрос помечен как решенный

Ответы 18

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

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

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

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

Ответ принят как подходящий

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

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

Кроме того, динамические библиотеки не обязательно загружаются - они обычно загружаются при первом вызове - и могут использоваться совместно компонентами, использующими одну и ту же библиотеку (несколько загрузок данных, одна загрузка кода).

В большинстве случаев динамические библиотеки считались лучшим подходом, но изначально у них был серьезный недостаток (ад Google DLL), который был почти устранен более поздними ОС Windows (в частности, Windows XP).

В Windows / Mac (без диспетчера пакетов) действительно нет веских причин использовать динамические библиотеки вместо статических. Поскольку библиотеки DLL Windows не могут быть перемещены, совместное использование кода часто не работает (и обычно каждое приложение в любом случае поставляется и использует свои собственные версии библиотеки). Единственное реальное преимущество состоит в том, что библиотеку легче обновлять.

Zifre 12.05.2009 01:02

Что конкретно вы имеете в виду под «перемещаемым»?

Orion Adrian 12.05.2009 18:05

на Mac я использую много динамических библиотек. например, в mac os x есть встраивание sqlite3. Я создал программу с функцией базы данных sqlite3 для сохранения производительности. однако, поскольку оно редко используется, динамическое связывание экономит время компиляции, упрощает / ускоряет тестирование, однако, если бы я создавал версию выпуска, я думаю, что всегда использовал бы статическую библиотеку только в случае проблем с совместимостью

ReachConnection 16.10.2009 23:34

@Zifre: relocatable = может быть загружен по другому виртуальному адресу. DLL, безусловно, поддерживает это.

dma_k 02.10.2010 16:35

@dma_k: библиотеки DLL Windows могут быть загружены по разным адресам, но только потому, что компоновщик копирует весь код и изменяет номера адресов. В случае общих объектов все адресные ссылки являются относительными, поэтому несколько процессов могут совместно использовать одну и ту же память для общего объекта. Другими словами, в Windows 1 МБ DLL, используемая 3 программами, = 3 МБ. В Linux размер SO, используемый 3 программами, равен 1 МБ.

Zifre 17.10.2010 05:15

И в Windows, и в Linux существует концепция перемещения разделяемых библиотек по расписанию eli.thegreenplace.net/2011/08/25/… Самая большая вещь, которая позволяла позиционно-независимый код, не была чем-то особенным для Linux, а скорее относительной адресацией RIP, добавленной с набором инструкций x64; как Windows, так и Linux могут использовать относительную адресацию RIP, уменьшая количество исправлений при перемещении библиотек.

clemahieu 03.08.2013 19:47

Статические библиотеки хуже взаимодействуют и их труднее использовать, например, в средах .NET.

Colin 16.10.2015 19:52

Стоит упомянуть, что иногда из-за лицензионных ограничений вам необходимо динамически связывать свои приложения с библиотеками, если вы хотите, чтобы ваша кодовая база была закрытой, например, Qt с LGPL.

Jämes 04.05.2017 18:42

Будет ли разница в производительности между StaticLib и SharedLib после полной загрузки кода в процессе?

baye 21.12.2020 13:31

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

Вы могли бы использовать DLL, если хотите иметь возможность изменять функциональные возможности, предоставляемые библиотекой, без необходимости повторно связывать исполняемый файл (просто замените файл DLL, не заменяя исполняемый файл).

Вы бы использовали статическую библиотеку всякий раз, когда у вас нет причин использовать динамическую библиотеку.

Вы также можете использовать DLL, когда несколько других приложений используют ту же функциональность - это может уменьшить занимаемую площадь.

Tim 27.04.2010 01:52

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

Tim 27.04.2010 01:54

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

Если библиотека статическая, то во время компоновки код связывается с вашим исполняемым файлом. Это делает ваш исполняемый файл больше (чем если бы вы пошли по динамическому маршруту).

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

Если вы можете жить со статической библиотекой, перейдите к статической библиотеке.

Библиотека - это единица кода, связанная с исполняемым файлом вашего приложения.

DLL - это автономная единица исполняемого кода. Он загружается в процесс только при вызове этого кода. DLL может использоваться несколькими приложениями и загружаться в несколько процессов, сохраняя при этом только одну копию кода на жестком диске.

Dll плюсы: может использоваться для повторного использования / совместного использования кода несколькими продуктами; загружается в память процесса по запросу и может выгружаться, когда не требуется; можно обновлять независимо от остальной программы.

Dll минусы: влияние загрузки dll и перебазирования кода на производительность; проблемы с версиями ("ад dll")

Либеральные профи: не влияет на производительность, поскольку код всегда загружается в процессе и не обновляется; нет проблем с версией.

Lib cons: исполняемый файл / процесс "раздувание" - весь код находится в вашем исполняемом файле и загружается при запуске процесса; нет повторного использования / обмена - у каждого продукта есть собственная копия кода.

Перебазирование также можно выполнить во время сборки с помощью rebase.exe или путем передачи параметра / BASE в link.exe. Эффективность этого метода зависит от того, возникнут ли какие-либо неожиданные конфликты адресного пространства во время выполнения.

bk1e 26.09.2008 20:13

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

Вам следует тщательно обдумать изменения с течением времени, управление версиями, стабильность, совместимость и т. д.

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

Или вы хотите изолировать их друг от друга, чтобы вы могли изменить одно и быть уверенным, что не сломали другое. Затем используйте статическую библиотеку.

DLL, черт возьми, это когда вам, вероятно, СЛЕДУЕТ использовать статическую библиотеку, но вместо этого вы использовали dll, и не все exes совместимы с ней.

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

Другие адекватно объяснили, что такое статическая библиотека, но я хотел бы указать на некоторые предостережения при использовании статических библиотек, по крайней мере, в Windows:

  • Синглтоны: Если что-то должно быть глобальным / статическим и уникальным, будьте очень осторожны, помещая это в статическую библиотеку. Если несколько DLL связаны с этой статической библиотекой, каждая из них получит свою собственную копию синглтона. Однако, если ваше приложение представляет собой один EXE-файл без пользовательских библиотек DLL, это может не быть проблемой.

  • Удаление кода без ссылки: При компоновке со статической библиотекой только те части статической библиотеки, на которые ссылается ваша DLL / EXE, будут связаны с вашей DLL / EXE.

    Например, если mylib.lib содержит a.obj и b.obj, а ваша DLL / EXE ссылается только на функции или переменные из a.obj, компоновщик полностью отбросит b.obj. Если b.obj содержит глобальные / статические объекты, их конструкторы и деструкторы не будут выполнены. Если у этих конструкторов / деструкторов есть побочные эффекты, вы можете быть разочарованы их отсутствием.

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

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

  • Символы отладки: Вам может понадобиться отдельный PDB для каждой статической библиотеки, или вы можете захотеть, чтобы символы отладки были помещены в объектные файлы, чтобы они были перенесены в PDB для DLL / EXE. Документация Visual C++ объясняет необходимые опции.

  • RTTI: Вы можете получить несколько объектов type_info для одного и того же класса, если соедините одну статическую библиотеку с несколькими библиотеками DLL. Если ваша программа предполагает, что type_info является «одноэлементными» данными и использует &typeid() или type_info::before(), вы можете получить нежелательные и неожиданные результаты.

Что касается синглтонов, не забывайте, что DLL может загружаться несколько раз (одна и та же версия или несколько версий), и по-прежнему нет гарантии синглтона.

Orion Adrian 19.03.2010 22:32

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

Orion Adrian 10.11.2010 01:16

@ bk1e Этого не должно происходить. .a всегда будет содержать все символы, с которыми он был построен. Когда он статически связан с вашим приложением, да, будут связаны только те символы, которые используются.

Miles Rout 29.08.2013 16:17

Для отличного обсуждения этой темы прочтите эта статья от Sun.

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

Статья Ульриха Дреппера о «Как писать общие библиотеки» также является хорошим ресурсом, в котором подробно описывается, как лучше всего использовать преимущества общих библиотек или то, что он называет «динамическими совместно используемыми объектами» (DSO). В нем больше внимания уделяется разделяемым библиотекам в двоичном формате ELF, но некоторые обсуждения подходят и для Windows DLL.

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

Например, если вы используете лицензионный код LGPL и статически связываетесь с библиотекой LGPL (и, таким образом, создаете один большой двоичный файл), ваш код автоматически становится открытым исходным кодом (код бесплатно как на свободе) LGPL. Если вы связываете с общими объектами, вам нужно только в LGPL - улучшения / исправления ошибок, внесенные вами в саму библиотеку LGPL.

Это становится гораздо более важной проблемой, если вы, например, решаете, как скомпилировать мобильные приложения (в Android у вас есть выбор между статическим и динамическим, в iOS - нет - он всегда статичен).


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

$$:~/static [32]> cat foo.c
#include<stdio.h>
void foo()
{
printf("\nhello world\n");
}
$$:~/static [33]> cat foo.h
#ifndef _H_FOO_H
#define _H_FOO_H

void foo();

#endif
$$:~/static [34]> cat foo2.c
#include<stdio.h>
void foo2()
{
printf("\nworld\n");
}
$$:~/static [35]> cat foo2.h
#ifndef _H_FOO2_H
#define _H_FOO2_H

void foo2();

#endif
$$:~/static [36]> cat hello.c
#include<foo.h>
#include<foo2.h>
void main()
{
foo();
foo2();
}
$$:~/static [37]> cat makefile
hello: hello.o libtest.a
        cc -o hello hello.o -L. -ltest
hello.o: hello.c
        cc -c hello.c -I`pwd`
libtest.a:foo.o foo2.o
        ar cr libtest.a foo.o foo2.o
foo.o:foo.c
        cc -c foo.c
foo2.o:foo.c
        cc -c foo2.c
clean:
        rm -f foo.o foo2.o libtest.a hello.o

$$:~/static [38]>

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

$$:~/dynamic [44]> cat foo.c
#include<stdio.h>
void foo()
{
printf("\nhello world\n");
}
$$:~/dynamic [45]> cat foo.h
#ifndef _H_FOO_H
#define _H_FOO_H

void foo();

#endif
$$:~/dynamic [46]> cat foo2.c
#include<stdio.h>
void foo2()
{
printf("\nworld\n");
}
$$:~/dynamic [47]> cat foo2.h
#ifndef _H_FOO2_H
#define _H_FOO2_H

void foo2();

#endif
$$:~/dynamic [48]> cat hello.c
#include<foo.h>
#include<foo2.h>
void main()
{
foo();
foo2();
}
$$:~/dynamic [49]> cat makefile
hello:hello.o libtest.sl
        cc -o hello hello.o -L`pwd` -ltest
hello.o:
        cc -c -b hello.c -I`pwd`
libtest.sl:foo.o foo2.o
        cc -G -b -o libtest.sl foo.o foo2.o
foo.o:foo.c
        cc -c -b foo.c
foo2.o:foo.c
        cc -c -b foo2.c
clean:
        rm -f libtest.sl foo.o foo

2.o hello.o
$$:~/dynamic [50]>

В нашем проекте мы используем много DLL (> 100). Эти DLL зависят друг от друга, поэтому мы выбрали настройку динамического связывания. Однако у него есть следующие недостатки:

  • медленный запуск (> 10 секунд)
  • DLL должны были иметь версии, так как Windows загружает модули по уникальности имен. В противном случае собственные написанные компоненты получили бы неправильную версию DLL (то есть уже загруженную вместо своего собственного распределенного набора)
  • оптимизатор может оптимизировать только в пределах DLL. Например, оптимизатор пытается разместить часто используемые данные и код рядом друг с другом, но это не будет работать через границы DLL.

Возможно, лучше было сделать все статической библиотекой (и, следовательно, у вас есть только один исполняемый файл). Это работает, только если не происходит дублирования кода. Тест, похоже, подтверждает это предположение, но я не смог найти официальную цитату MSDN. Так, например, создайте 1 exe с помощью:

  • exe использует shared_lib1, shared_lib2
  • shared_lib1 используйте shared_lib2
  • shared_lib2

Код и переменные shared_lib2 должны присутствовать в окончательном объединенном исполняемом файле только один раз. Кто-нибудь может поддержать этот вопрос?

Разве вы не хотели каким-то образом использовать некоторые предварительные директивы компилятора, чтобы избежать дублирования кода?

Paceman 24.11.2013 05:53

Предварительная компиляция Afaiac работает только для отдельных модулей (exe / dll / lib). Предварительная компиляция в первую очередь предназначена для ускорения компиляции, хотя она также предотвращает множественные включения в единицу компиляции. Однако включить охранников - лучший способ добиться этого эффекта.

gast128 26.06.2015 18:28

Я бы дал общее практическое правило: если у вас большая кодовая база, построенная на основе библиотек нижнего уровня (например, фреймворка Utils или Gui), которые вы хотите разделить на более управляемые библиотеки, а затем сделайте их статическими библиотеками. Динамические библиотеки на самом деле ничего вам не покупают, и сюрпризов меньше - например, будет только один экземпляр синглтонов.

Если у вас есть библиотека, которая полностью отделена от остальной части кодовой базы (например, сторонняя библиотека), подумайте о том, чтобы сделать ее dll. Если это библиотека LGPL, вам может потребоваться использовать dll из-за условий лицензирования.

Программы на C++ строятся в два этапа

  1. Компиляция - создает объектный код (.obj)
  2. Связывание - создает исполняемый код (.exe или .dll)

Статическая библиотека (.lib) - это просто набор файлов .obj и поэтому не является полной программой. Не прошел второй (связывающий) этап построения программы. С другой стороны, DLL похожи на exe и, следовательно, представляют собой законченные программы.

Если вы создаете статическую библиотеку, она еще не связана, и поэтому потребителям вашей статической библиотеки придется использовать тот же компилятор, который вы использовали (если вы использовали g ++, им придется использовать g ++).

Если вместо этого вы создали dll (и построили ее правильно), вы создали полную программу, которую могут использовать все потребители, независимо от того, какой компилятор они используют. Однако существует несколько ограничений на экспорт из dll, если требуется совместимость с кросс-компилятором.

Это для меня новость. Какие ограничения есть у кросс-компиляторов при использовании DLL? Сборка программиста без использования того же набора инструментов кажется огромным плюсом для DLL.

Dan 18.04.2018 19:09

Это информативный ответ. Добавление небольшого предостережения: consumers of your static library will have to use the same compiler that you used, если статическая библиотека использует библиотеку C++, например #include <iostream>.

eigenfield 29.03.2020 07:21

нельзя использовать C++ dll, если не используется тот же компилятор (поскольку нет стандартного C++ abi, символы искажаются по-разному). И dll, и клиентский модуль должны использовать один и тот же компилятор и одинаковые настройки сборки.

kcris 24.05.2020 16:50

Другие вопросы по теме