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

437

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

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

Ответы:

299

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

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

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

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

Орион Адриан
источник
71
В Windows / Mac (без менеджера пакетов) действительно нет веской причины использовать динамические библиотеки вместо статических. Поскольку библиотеки DLL Windows нельзя перемещать, совместное использование кода часто не работает (и обычно каждое приложение поставляется и использует свои собственные версии библиотеки в любом случае). Единственное реальное преимущество - проще обновлять библиотеку.
Zifre
5
на Mac я использую много динамических библиотек. Например, Mac OS X имеет встроенный sqlite3. я создал программу, которая имеет функцию базы данных sqlite3 для хранения производительности. однако, поскольку он редко используется, динамическое связывание экономит время компиляции, делает тестирование проще / быстрее, однако, если бы я
собирал выпускную
6
@Zifre: relocatable = может быть загружен по другому виртуальному адресу. DLL, безусловно, поддерживает это.
dma_k
20
@dma_k: Windows DLL могут быть загружены по разным адресам, но только потому, что компоновщик копирует весь код и изменяет номера адресов. С общими объектами все ссылки на адреса являются относительными, поэтому несколько процессов могут совместно использовать одну и ту же память для общего объекта. Другими словами, в Windows библиотека DLL размером 1 МБ, используемая 3 программами, = 3 МБ. В Linux A MB SO используется 3 программами = 1 МБ.
Zifre
7
Как в Windows, так и в Linux есть концепция временного перемещения разделяемых библиотек eli.thegreenplace.net/2011/08/25/… Самая большая вещь, которая позволила позиционировать независимый код, не была чем-то особенным для Linux, добавлена ​​адресация, относящаяся к RIP с набором команд x64; Как в Windows, так и в Linux можно использовать относительную адресацию RIP, что уменьшает количество исправлений при перемещении библиотек.
Clemahieu
194

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

  • Singletons: Если что-то должно быть глобальным / статическим и уникальным, будьте очень осторожны, помещая это в статическую библиотеку. Если несколько статических библиотек связаны с этой статической библиотекой, каждая из них получит свою собственную копию синглтона. Однако, если ваше приложение представляет собой один 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(), вы можете получить нежелательные и неожиданные результаты.

bk1e
источник
23
Что касается вопроса о синглетах, не забывайте, что DLL может быть загружена несколько раз (одна и та же версия или несколько версий), и пока еще нет однозначной гарантии.
Орион Адриан
Дополнительный пункт об удалении ссылочного кода: вызовы, сделанные в DLL, также требуют фактического вызова для принудительной загрузки ссылочной DLL. Добавление его в качестве ссылки, но затем без учета вызовов, ссылающихся на него, все равно даст вам тот же результат, что и статический либарий, который ничего не вызывает. Разница лишь в том, что на самом деле корабли. В обоих случаях статические конструкторы и деструкторы не срабатывают.
Орион Адриан
@ bk1e Этого не должно быть. .a всегда будет содержать все символы, с которыми он был построен. Когда он статически связан с вашим приложением, да, в него будут включены только те символы, которые используются.
Miles Rout
62

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

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

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

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

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

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

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

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

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

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

rburhum
источник
23

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

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

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

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

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

УТС
источник
1
Это для меня новость. Какие ограничения существуют для кросс-компиляторов при использовании DLL? Наличие сборки для программиста без необходимости использования одной и той же цепочки инструментов кажется огромным плюсом для библиотек DLL
Дан
1
Этот ответ является информативным. Добавление незначительного предостережения: consumers of your static library will have to use the same compiler that you usedесли статическая библиотека использует библиотеку C ++, например #include <iostream>.
Правда от
нельзя использовать dll c ++, если не используется один и тот же компилятор (поскольку нет стандартного abi c ++, символы искажаются по-разному). И модуль DLL, и клиентский модуль должны использовать один и тот же компилятор и одинаковые параметры сборки
kcris
19

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

$$:~/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]>
Виджай
источник
13

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

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

Джордан Пармер
источник
13

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

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

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

DLL ад - это когда вы, вероятно, ДОЛЖНЫ ИСПОЛЬЗОВАТЬ статическую библиотеку, но вместо этого вы использовали dll, и не все exes совместимы с ней.

Кори Трагер
источник
9

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

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

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

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

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

пустота
источник
5

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

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

Роб Уэллс
источник
4

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

pfranza
источник
3

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

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

Дима
источник
3

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

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

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

Себ Роуз
источник
3

Мы используем много 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 должны присутствовать в конечном объединенном исполняемом файле только один раз. Кто-нибудь может поддержать этот вопрос?

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

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

Теренс Симпсон
источник
2

Если вы работаете над встроенными проектами или специализированными платформами, статические библиотеки - это единственный путь, а также зачастую они не так сложны для компиляции в ваше приложение. Также наличие проектов и makefile, которые включают все, делает жизнь счастливее.

Роберт Гулд
источник
2

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

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

the_mandrill
источник