Почему вы должны связать математическую библиотеку в C? - PullRequest
229 голосов
/ 23 июня 2009

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

gcc test.c -o test -lm

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

Ответы [ 11 ]

220 голосов
/ 23 июня 2009

Функции в 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, связанный с ней.

71 голосов
/ 23 июня 2009

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

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

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

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

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

68 голосов
/ 05 января 2011

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

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

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

33 голосов
/ 05 января 2011

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

26 голосов
/ 23 июня 2009

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

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

[Изменить]

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

5 голосов
/ 23 июня 2009

Как сказал 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.

4 голосов
/ 23 июня 2009

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

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

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

3 голосов
/ 23 июня 2009

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

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

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

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

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

3 голосов
/ 23 июня 2009

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

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

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

2 голосов
/ 23 июня 2009

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

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

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

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

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

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

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

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

...