Почему вы должны связать математическую библиотеку в C?

254

Если я включаю <stdlib.h>или <stdio.h>в программу на C, мне не нужно связывать их при компиляции, но я должен ссылаться , например <math.h>, с -lmпомощью gcc:

gcc test.c -o test -lm

Что является причиной этого? Почему я должен явно связать математическую библиотеку, а не другие библиотеки?

Нет
источник

Ответы:

249

Функции в stdlib.hи stdio.hимеют реализации в libc.so(или libc.aдля статической компоновки), которая по умолчанию связана с вашим исполняемым файлом (как если бы они -lcбыли указаны). GCC можно поручить избегать этой автоматической связи с опциями -nostdlibили -nodefaultlibs.

Математические функции в math.hимеют реализации в libm.so(или libm.aдля статического связывания) и libmпо умолчанию не связаны. Есть исторические причины для этого libm/ libcраскола, ни одна из них не очень убедительна.

Интересно, что среда выполнения C ++ libstdc++требует libm, поэтому, если вы скомпилируете программу C ++ с помощью GCC ( g++), вы автоматически получите libmссылку.

ephemient
источник
8
Это не имеет ничего общего с Linux, так как это было распространено задолго до Linux. Я подозреваю, что это как-то связано с попыткой минимизировать размер исполняемого файла, так как есть много программ, которым не нужны математические функции.
Дэвид Торнли
39
В древних системах, если бы математические функции содержались в libc, то компиляция всех программ была бы медленнее, выходные исполняемые файлы были бы больше, а время выполнения требовало бы больше памяти, без какой-либо выгоды для большинства программ, которые вообще не используют эти математические функции. В наши дни у нас есть хорошая поддержка общих библиотек, и даже при статическом связывании стандартные библиотеки настраиваются так, что неиспользуемый код может быть отброшен, поэтому ни одна из этих причин больше не является веской.
Эфимент
38
@ephemient Даже в старые времена ссылки на библиотеку не вытягивали все содержимое библиотеки в исполняемый файл. Линкеры, хотя технология часто игнорируется, исторически были достаточно эффективными.
7
@ephemient Кроме того, общие библиотеки существуют дольше, чем вы думаете. Они были изобретены в 1950-х, а не в 1980-х.
5
Я полагаю, в конце концов, то, на что мы смотрим, является не чем иным, как консерватизмом GCC: «это всегда работает так». Я только хотел бы, чтобы они применили те же аргументы к своим расширениям компилятора.
77

Помните, что C является старым языком, а FPU - явление относительно недавнее. Я впервые увидел C на 8-битных процессорах, где было много работы, чтобы выполнить даже 32-битную целочисленную арифметику. Многие из этих реализаций даже не имели доступной математической библиотеки с плавающей точкой!

Даже на первых 68000 машин (Mac, Atari ST, Amiga) сопроцессоры с плавающей запятой часто были дорогими дополнениями.

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

Компромиссы существовали в течение длительного времени. Иногда встречались конкурирующие математические пакеты, называемые «fastmath» или подобные. Какое лучшее решение для математики? Действительно точный, но медленный материал? Неточно, но быстро? Большие таблицы для тригонометрических функций? Лишь в тех случаях, когда сопроцессоры были гарантированно находиться в компьютере, большинство реализаций стало очевидным. Я предполагаю, что где-то прямо сейчас есть какой-то программист, работающий над встроенным чипом и пытающийся решить, стоит ли вводить математическую библиотеку для решения какой-то математической задачи.

Вот почему математика не была стандартной . Многие или, может быть, большинство программ не использовали один float. Если бы FPU всегда были рядом, а поплавки и парные удары всегда были дешевы в эксплуатации, без сомнения, это был бы "удар".

Nosredna
источник
Хех, я использую аппроксимации Паде для (1 + x) ^ y в Java на настольном ПК. Log, exp и pow все еще медленные.
Quant_dev
Хорошая точка зрения. И я видел приближения для sin () в аудио плагинах.
Носредна
11
Это объясняет, почему libmпо умолчанию это не связано, но математика была стандартной для C89, а до этого K & R де-факто стандартизировала ее, поэтому ваше замечание о "stdmath" не имеет смысла.
Фред Фу
@FredFoo Типы и интерфейсы были стандартизированы, но не реализованы. Я думаю, что Носредна имеет в виду стандартную математическую библиотеку.
Тим Берд
72

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

Реализация которой все функции находятся в одной библиотеке и -lm, -lpthread, -lrtи т.д. опции не все нет-OPS (или ссылка на пустые .aфайлы) идеально POSIX совместимого и , конечно , предпочтительнее.

Примечание: я говорю о POSIX, потому что сам C не определяет ничего о том, как вызывается компилятор. Таким образом, вы можете просто рассматривать gcc -std=c99 -lmкак зависящий от реализации способ, которым компилятор должен вызываться для согласованного поведения.

R .. GitHub СТОП, ПОМОГАЯ ЛЬДУ
источник
9
+1 за указание на то, что POSIX не требует существования отдельных библиотек libm, libc и librt. Например, в Mac OS все находится в одной libSystem (которая также включает libdbm, libdl, libgcc_s, libinfo, libm, libpoll, libproc и librpcsvc).
F'x
3
-1 для размышлений о влиянии поиска в библиотеке на производительность без резервного копирования со ссылкой или цифрами. «Профиль. Не спекулируйте»
F'x
12
Это не спекуляция. У меня нет опубликованных работ, но я сам провел все измерения, и разница огромна. Просто используйте straceодин из вариантов синхронизации, чтобы посмотреть, сколько времени запуска затрачивается на динамическое связывание, или сравните работу ./configureв системе, где все стандартные утилиты статически связаны, и одну, где они динамически связаны. Даже основные разработчики настольных приложений и системные интеграторы знают о стоимости динамического связывания; Вот почему существуют такие вещи, как prelink. Я уверен, что вы можете найти ориентиры в некоторых из этих работ.
R .. GitHub ОСТАНОВИТЬ ЛЬДА
1
Обратите внимание , что POSIX делает требуют , -lmчтобы быть принятым и приложения , которые используют математические интерфейсы должны использовать -lm, но это может быть внутренний параметр обрабатывается (или даже игнорируется) с помощью команды компилятора, а не фактический файл библиотеки. Или это может быть просто пустой .aфайл, если интерфейсы находятся в основном libc.
R .. GitHub ОСТАНОВИТЬ ЛЬДА
6
@FX: Не знаю, почему я забыл упомянуть об этом раньше: strace -ttлегко покажет вам время, потраченное на динамические ссылки. Это не красиво. А в Linux проверка /proc/sys/smapsпокажет вам дополнительные ресурсы памяти дополнительных библиотек.
R .. GitHub ОСТАНОВИТЬ ЛЬДА
33

Потому что time()и некоторые другие функции builtinопределены в самой C library ( libc), и GCC всегда ссылается на libc, если вы не используете -ffreestandingопцию compile. Однако математические функции живут в libmкоторых неявно не связаны gcc.

исмаил
источник
8
На LLVM GCC мне не нужно добавлять -lm. Почему это?
bot47
26

Объяснение дается здесь :

Так что, если ваша программа использует математические функции и в том числе math.h, то вам нужно явно связать математическую библиотеку, передав -lmфлаг. Причина этого конкретного разделения состоит в том, что математики очень разборчивы в способе вычисления их математики, и они могут захотеть использовать свою собственную реализацию математических функций вместо стандартной реализации. Если бы математические функции были объединены, libc.aэто было бы невозможно сделать.

[Редактировать]

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

Бастьен Леонар
источник
10
Я не думаю, что есть гарантия, что это произойдет; вместо этого вы можете столкнуться с конфликтом символов. Вероятно, это будет зависеть от компоновщика и макета библиотеки. Я все еще нахожу эту причину слабой; если вы создаете пользовательскую функцию sqrt, вам действительно не следует давать ей то же имя, что и у стандартной функции sqrt, даже если она делает то же самое ...
ephemient
1
Действительно, создание собственной функции (нестатической) с именем sqrtприводит к программе с неопределенным поведением.
R .. GitHub ОСТАНОВИТЬ ЛЬДА
@Bastien Хорошая находка. И что касается вашей точки зрения, что вы подразумеваете под «до стандартной библиотеки»? Я думал, что стандартная библиотека связана по умолчанию и не требуется связывать через параметры командной строки. Таким образом, стандартная библиотека станет первым средством для компоновщика, и никто не сможет разместить свою собственную реализацию «перед стандартной библиотекой».
Рокки Инд
@RockyInde: посмотрите на мой ответ, я думаю, что имел в виду «до стандартной математической библиотеки». Но я думаю, что есть опции компилятора, которые не связывают стандартную библиотеку C, что позволит вам передать вашу.
Бастьен Леонар
@ BastienLéonard Я использую gcc версии 7.2, что -lmсовершенно необязательно. Любые идеи
Донхуа Лю
5

Подробное обсуждение ссылок на внешние библиотеки приведено в разделе «Введение в GCC - Связь с внешними библиотеками» . Если библиотека является членом стандартных библиотек (например, stdio), вам не нужно указывать компилятору (на самом деле компоновщику), чтобы связать их.

РЕДАКТИРОВАТЬ: После прочтения некоторых других ответов и комментариев, я думаю, что ссылка на libc.a и ссылка на libm, на которую она ссылается, может многое сказать о том, почему оба они разделены.

Обратите внимание, что многие функции из libm.a (математической библиотеки) определены в math.h, но отсутствуют в libc.a. Некоторые из них могут сбивать с толку, но практическое правило таково: библиотека C содержит те функции, которые должны существовать в соответствии с требованиями ANSI, поэтому вам не нужен ключ -lm, если вы используете только функции ANSI. Напротив, libm.a содержит больше функций и поддерживает дополнительные функции, такие как обратный вызов matherr и соответствие нескольким альтернативным стандартам поведения в случае ошибок FP. Смотрите раздел libm, для более подробной информации.

Билл Ящерица
источник
1
Что не отвечает на вопрос, почему вы должны ссылаться в библиотеках совпадений отдельно. Очевидно, что вы хотите связать библиотеки OpenGL отдельно, но, возможно, математические библиотеки обычно полезны.
Дэвид Торнли
@ Дэвид: Ты прав. Из вопроса мне было непонятно, что именно об этом спрашивал ОП. Я редактировал мой ответ, как вы прокомментировали.
Билл Ящерица
Я знаю причину, по которой я скомпилировал программу, которая использует эту sqrtфункцию, и она работает без включения библиотеки через -lm. Спасибо!
L_K
5

Как сказал ephemient, библиотека C libc по умолчанию связана, и эта библиотека содержит реализации stdlib.h, stdio.h и нескольких других стандартных заголовочных файлов. Просто добавьте к этому, в соответствии с « Введение в GCC » команда компоновщика для базовой программы «Hello World» на C выглядит следующим образом:

ld -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o 
/usr/lib/crti.o /usr/libgcc-lib /i686/3.3.1/crtbegin.o
-L/usr/lib/gcc-lib/i686/3.3.1 hello.o -lgcc -lgcc_eh -lc 
-lgcc -lgcc_eh /usr/lib/gcc-lib/i686/3.3.1/crtend.o /usr/lib/crtn.o

Обратите внимание на опцию -lc в третьей строке, которая связывает библиотеку C.

ardsrk
источник
3

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

Это дает вам возможность заменить его другим, имеющим те же функции, но я не думаю, что это очень распространено.

РЕДАКТИРОВАТЬ: (из моих собственных комментариев): я думаю, что gcc делает это для обеспечения обратной совместимости с оригинальным cc. Я предполагаю, почему cc делает это из-за времени сборки - cc был написан для машин с гораздо меньшей мощностью, чем у нас сейчас. У многих программ нет математики с плавающей точкой, и они, вероятно, взяли все библиотеки, которые обычно не использовались, по умолчанию. Я предполагаю, что время сборки ОС UNIX и соответствующие инструменты были движущей силой.

Лу Франко
источник
Я думаю, что вопрос заключается в том, что содержимое libm в основном является частью стандартной библиотеки C, почему они не находятся в libc?
Эван Теран
1
Почему gcc поддерживает совместимость с оригинальным cc в AT & T Unix. Я использовал 3B2 в 1988 году, и вам пришлось -lm, чтобы получить математику. В то время это казалось мне совершенно произвольным. В Visual Studio я не помню, чтобы когда-либо приходилось добавлять математику, но иногда приходится добавлять другие, казалось бы, библиотеки c-runtime. Я предполагаю, что у поставщиков компиляторов есть причина (время сборки?), Но прямо сейчас я уверен, что gcc просто пытается быть обратно совместимым.
Лу Франко
3

Если я добавлю stdlib.h или stdio.h, мне не нужно связывать их, но я должен связать их при компиляции:

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

Включение math.h- это только первый шаг к получению доступа ко всем математическим функциям.

Кроме того, вам не нужно ссылаться на них, libmесли вы не используете их функции, даже если вы делаете #include <math.h>для вас только информационный шаг для компилятора символов.

stdlib.h, stdio.hОтносятся к функциям доступны в libc, что случается, всегда связаны с тем , что пользователь не должен делать это сам.

Адриан Панасюк
источник
2

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

Реализации математической функции находятся в отдельном файле libm, который не связан по умолчанию, поэтому вы должны указать его -lm. Кстати, между этими заголовочными файлами и библиотеками нет никакой связи.


источник
3
он знает, что .. он спрашивает, почему
Эван Теран
Он говорит почему. Саймон объясняет, что некоторые библиотеки связаны по умолчанию, например, stdio, тогда как математическая библиотека не связана по умолчанию, поэтому ее нужно указать.
Mnuzzo
5
Я бы сказал, что суть вопроса заключается в том, почему libm не связан по умолчанию (или даже отделен от libc), поскольку его содержимое в значительной степени является частью стандартной библиотеки c.
Эван Теран
2

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

Операционные системы x86 (и я представляю другие) должны хранить состояние FPU при переключении контекста. Тем не менее, большинство ОС пытаются сохранить / восстановить это состояние только после того, как приложение попытается использовать FPU в первый раз.

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

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

Просто предположение, хотя.

РЕДАКТИРОВАТЬ: в ответ на некоторые комментарии, та же самая базовая предпосылка все еще применяется к случаям не-FPU (предпосылка состоит в том, чтобы сделать приложения, которые не используют libm, работают немного лучше).

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

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

Эван Теран
источник
Если вы не связываетесь с libm, а касаетесь FPU x87 с помощью других средств (например, операций с числами с плавающей запятой), ядру x86 необходимо сохранить состояние FPU. Я не думаю, что это очень хорошее предположение ...
Эфимент
конечно, если вы используете FPU вручную, ядру все равно нужно будет сохранить / восстановить его состояние. Я говорил, что если вы никогда не используете его (в том числе не используете libm), то это не обязательно.
Эван Теран
На самом деле это может очень сильно зависеть от ядра. Математическая библиотека, которую использует ядро, может иметь функцию save_FPU_on_switch (), которая включает ее, в то время как другие просто обнаруживают, касался ли FPU.
Эрлз
1
Если я не ошибаюсь, весь выпуск задолго до появления сопроцессоров с плавающей запятой, даже на микропроцессорах.
Носредна
@earlz: подход к сохранению запроса математической библиотеки был бы ужасным дизайном. Что если они используют FPU другими способами? Единственный разумный подход (помимо просто всегда сохранения / восстановления) - обнаружение использования, а затем начало сохранения / восстановления.
Эван Теран