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

185

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

gcc test.c -o test -lm

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

Теги:
compilation
math.h

9 ответов

192
Лучший ответ

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

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

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

  • 0
    Итак, это становится вопросом дизайна библиотеки - почему библиотеки разделены таким образом?
  • 1
    Бьюсь об заклад, чтобы оптимизировать время сборки самого UNIX (и набор инструментов, который шел вместе с ним). В то время это была, вероятно, самая сложная вещь, которая была построена в C.
Показать ещё 15 комментариев
60

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

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

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

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

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

  • 0
    Хех, я использую аппроксимации Паде для (1 + x) ^ y в Java на настольном ПК. Log, exp и pow все еще медленные.
  • 0
    Хорошая точка зрения. И я видел приближения для sin () в аудио плагинах.
Показать ещё 1 комментарий
23

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

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

[изменить]

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

  • 10
    Я не думаю, что есть гарантия, что это произойдет; вместо этого вы можете столкнуться с конфликтом символов. Вероятно, это будет зависеть от компоновщика и макета библиотеки. Я все еще нахожу эту причину слабой; если вы создаете пользовательскую функцию sqrt, вам не следует давать ей то же имя, что и стандартной функции sqrt, даже если она делает то же самое ...
  • 1
    Действительно, создание собственной функции (нестатической) с именем sqrt приводит к программе с неопределенным поведением.
Показать ещё 3 комментария
5

Как сказано выше, библиотека C lib lib связана по умолчанию, и эта библиотека содержит реализации 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.

3

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

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

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

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

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

3

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

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

EDIT: (из моих собственных комментариев): Я думаю, что gcc делает это, чтобы поддерживать обратную совместимость с оригинальным cc. Мое предположение, почему cc делает это из-за времени сборки - cc был написан для машин с гораздо меньшей мощностью, чем у нас сейчас. У многих программ нет математики с плавающей запятой, и они, вероятно, взяли каждую библиотеку, которая обычно не использовалась из-за умолчания. Я предполагаю, что время сборки ОС UNIX и инструменты, которые идут вместе с ним, были движущей силой.

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

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

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

  • 3
    он знает что .. он спрашивает почему
  • 1
    Это не объясняет "почему"?
Показать ещё 2 комментария
3

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

EDIT: после прочтения некоторых других ответов и комментариев, я думаю, ссылка libc.a и ссылка libm, с которой она ссылается у обоих есть что рассказать о том, почему эти два являются отдельными.

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

  • 1
    Что не отвечает на вопрос, почему вы должны ссылаться в библиотеках совпадений отдельно. Очевидно, что вам нужно отдельно связывать библиотеки OpenGL, но, возможно, математические библиотеки обычно полезны.
  • 0
    @ Дэвид: Ты прав. Из вопроса мне было непонятно, что именно об этом спрашивал ОП. Я редактировал мой ответ, как вы прокомментировали.
Показать ещё 1 комментарий
2

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

Операционные системы

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

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

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

Только предположение.

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

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

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

  • 0
    Если вы не связываетесь с libm, а касаетесь FPU x87 с помощью других средств (например, операций с числами с плавающей запятой), ядру x86 необходимо сохранить состояние FPU. Я не думаю, что это очень хорошее предположение ...
  • 0
    Конечно, если вы используете FPU вручную, ядру все равно нужно будет сохранить / восстановить его состояние. Я говорил, что если вы никогда не используете его (в том числе не используете libm), то это не обязательно.
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню