Чому вам потрібно зв’язати математичну бібліотеку на 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зв’яжетесь.


8
Це не має нічого спільного з Linux, оскільки це було звичайно задовго до Linux. Я підозрюю, що це має відношення до спроби мінімізувати розмір виконуваних файлів, оскільки існує багато програм, які не потребують математичних функцій.
Девід Торнлі

39
У стародавніх системах, якщо математичні функції містилися в libc, то компіляція всіх програм буде повільнішою, вихідні виконувані файли будуть більшими, а час виконання вимагатиме більше пам'яті, без користі для більшості програм, які взагалі не використовують ці математичні функції. У наші дні ми маємо гарну підтримку спільних бібліотек, і навіть при стаціонарному посиланні стандартні бібліотеки налаштовані так, що невикористаний код можна буде відкинути, тому жодна з цих причин вже не є вагомою причиною.
ефемія

38
@ephemient Навіть у старі часи посилання на бібліотеку не перетягувало весь вміст бібліотеки до виконуваного файлу. Лінкери, хоча часто ігноруються технології, історично були досить ефективними.

7
@ephemient Також спільні бібліотеки існують довше, ніж ви могли подумати. Вони були винайдені у 1950-х, а не у 1980-х роках.

5
Я думаю, наприкінці дня те, що ми дивимось, є не що інше, як консерватизм GCC: "це завжди працює так". Я лише хочу, щоб вони застосували ті ж міркування до своїх розширень компілятора.

77

Пам'ятайте, що C - це стара мова і що ППУ - явище порівняно недавно. Я вперше побачив C на 8-бітових процесорах, де було багато роботи навіть для 32-бітової цілочисельної арифметики. Багато з цих реалізацій навіть не було бібліотеки математики з плаваючою комою!

Навіть на перших 68000 машинах (Mac, Atari ST, Amiga) співпроцесори з плаваючою комою часто були дорогими додатками.

Щоб зробити все з математики з плаваючою комою, вам знадобилася досить велика бібліотека. І математика збиралася повільною. Так ви рідко використовували поплавці. Ви намагалися зробити все з цілими чи масштабованими цілими числами. Коли вам довелося включати math.h, ви стискали зуби. Часто ви пишете власні наближення та таблиці пошуку, щоб уникнути цього.

Компроміси існували тривалий час. Іноді існували конкуруючі математичні пакети під назвою "fastmath" або подібні. Що найкраще рішення для математики? Дійсно точні, але повільні речі? Неточний, але швидкий? Великі таблиці для триггерних функцій? Лише коли копроцесори не отримали гарантії перебування в комп'ютері, більшість реалізацій стали очевидними. Я думаю, що там десь зараз є якийсь програміст, який працює над вбудованим чіпом, намагаючись вирішити, чи занести до математичної бібліотеки для вирішення якоїсь математичної проблеми.

Тому математика не була стандартною . Багато або, можливо, більшість програм не використовували один плавець. Якби FPU завжди були поруч, а поплавки і дублі завжди були дешевими для експлуатації, без сумніву, було б "stdmath".


Хе, я використовую наближення Pade для (1 + x) ^ y на Java, у настільному ПК. Журнал, 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як специфічний для способу використання компілятор для відповідної поведінки.


9
+1 для вказівки, що POSIX не вимагає існування відокремлених бібліотек libm, libc та librt. Наприклад, у Mac OS все розташоване в єдиній libSystem (до якої також входять libdbm, libdl, libgcc_s, libinfo, libm, libpoll, libproc та librpcsvc).
F'x

3
–1 для роздумів про вплив пошуку бібліотеки на продуктивність без резервного копіювання за допомогою посилання чи чисел. "Профіль. Не
спекулюйте

12
Це не спекуляція. У мене немає жодних опублікованих робіт, але я все зробив вимірювання сам, і різниця величезна. Просто скористайтеся straceодним із варіантів синхронізації, щоб переглянути, скільки часу запускається на динамічне зв’язування, або порівняйте роботу ./configureв системі, де всі стандартні утиліти є статичними, а не ті, де вони є динамічно пов'язаними. Навіть розробники настільних додатків та системні інтегратори знають про витрати на динамічні зв'язки; ось чому існують такі речі, як попереднє посилання. Я впевнений, що ви можете знайти орієнтири в деяких із цих робіт.
R .. GitHub СТОП ДОПОМОГАЙТЕ

1
Зверніть увагу , що POSIX робить вимагають , -lmщоб бути прийнятим і додатки , які використовують математичні інтерфейси повинні використовувати -lm, але це може бути внутрішній параметр обробляється (або навіть ігнорується) з допомогою команди компілятора, а не фактичне файл бібліотеки. Або це може бути просто порожній .aфайл, якщо інтерфейси знаходяться в основному libc.
R .. GitHub СТОП ДОПОМОГАЙТЕ

6
@FX: Не знаю, чому я забув згадати про це раніше: strace -ttлегко покаже вам час, витрачений на динамічне посилання. Це не дуже. А в Linux інспекція /proc/sys/smapsпокаже вам обсяг пам'яті додаткових бібліотек.
R .. GitHub ЗАСТАНОВИТИ ДІЙ

33

Оскільки time()і деякі інші функції builtinвизначені в самій бібліотеці C ( libc), а GCC завжди посилається на libc, якщо ви не використовуєте параметр -ffreestandingкомпіляції. Однак математичні функції живуть, в libmяких невірно пов'язана gcc.


8
У LLVM gcc мені не потрібно додавати -lm. Чому це?
bot47

26

Пояснення дано тут :

Отже, якщо у вашій програмі використовуються математичні функції та в тому числі math.h, вам потрібно явно зв’язати математичну бібліотеку, передавши -lmпрапор. Причиною такого розмежування є те, що математики дуже вибагливі до того, як обчислюється їх математика, і вони можуть захотіти використовувати власну реалізацію математичних функцій замість стандартної реалізації. Якби математичні функції були зібрані, libc.aце зробити це було б неможливо.

[Редагувати]

Я не впевнений, що згоден з цим. Якщо у вас є бібліотека, яка забезпечує, скажімо, sqrt()і передаєте її перед стандартною бібліотекою, Unix-лінкер прийме вашу версію, правда?


10
Я не думаю, що є гарантія, що це станеться; у вас може виникнути конфлікт символів. Ймовірно, це залежатиме від лінкера та компонування бібліотеки. Я все ще вважаю, що причина є слабкою; якщо ви створюєте власну функцію sqrt, ви дійсно не повинні давати їй те саме ім'я, що і стандартна функція sqrt, навіть якщо вона робить те саме ...
ephemient

1
Дійсно, створення власної функції (нестатичної) названих sqrtрезультатів у програмі з невизначеною поведінкою.
R .. GitHub СТОП ДОПОМОГАТИ

@Bastien Хороша знахідка. І підійшовши до вашої точки зору, що ви маєте на увазі під «стандартною бібліотекою»? Я подумав, що стандартна бібліотека пов'язана за замовчуванням і не обов'язково пов'язувати її через параметри командного рядка. Отже, стандартна бібліотека буде першим переходом до лінкера, і не можна розмістити власну реалізацію "перед стандартною бібліотекою".
Rocky Inde

@RockyInde: поглянь на мою відповідь, я думаю, що я насправді мав на увазі «перед стандартною математикою бібліотеки». Але я думаю, що є варіанти компілятора, щоб не зв’язувати стандартну бібліотеку С, що дозволило б передати свою.
Бастієн Леонард

@ BastienLéonard Я використовую gcc версії 7.2, що -lmабсолютно не є обов'язковим. Будь-які ідеї
Donghua Лю

5

Існує ретельна дискусія про зв'язок із зовнішніми бібліотеками у Вступі до GCC - Зв'язок із зовнішніми бібліотеками . Якщо бібліотека є членом стандартних бібліотек (як, наприклад, stdio), то вам не потрібно вказувати компілятору (справді лінкеру), щоб пов’язати їх.

EDIT: Прочитавши деякі інші відповіді та коментарі, я думаю, що посилання на libc.a та посилання на libm, які вона посилається на обидва, мають багато що сказати про те, чому обидві є окремими.

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


1
Що не відповідає на питання, чому вам доведеться окремо зв’язувати бібліотеки відповідностей. Очевидно, що ви хочете мати зв'язок бібліотек OpenGL окремо, але, мабуть, математичні бібліотеки, як правило, корисні.
Девід Торнлі

@David: Право ти є. З питання мені не було зрозуміло, що це було те, про що просила ОП. Я редагував свою відповідь, коли ви коментували.
Білл Ящірка

Я знаю причину, коли я склав програму, яка використовує sqrtфункцію, і вона працює без включення бібліотеки через -lm. Дякую!
L_K

5

Як було сказано ефектом, libc бібліотеки C пов'язаний за замовчуванням, і ця бібліотека містить реалізацію stdlib.h, stdio.h та декілька інших стандартних файлів заголовків. Тільки додати до нього, згідно з " Введенням до GCC " команда linker для основної програми "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

Я думаю, що це щось довільно. Ви повинні десь намалювати рядок (які бібліотеки за замовчуванням і які потрібно вказати).

Це дає вам можливість замінити його на інший, який має ті самі функції, але я не думаю, що це дуже часто буває.

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


Я думаю, що менталітет, що стоїть за питанням, полягає в тому, що вміст libm є значною мірою частиною стандартної бібліотеки С, чому вони не в libc?
Еван Теран

1
Причиною для gcc є підтримка сумісності з оригінальним cc в AT&T Unix. Я використовував 3B2 в 1988 році, і вам довелося -mm, щоб отримати математику. Мені це здавалося зовсім довільним. У Visual Studio я не пам'ятаю, щоб коли-небудь доводилося додавати математику, але іноді доводиться додавати інші, здавалося б, бібліотеки c-час виконання. Я припускаю, що у постачальників компілятора є причина (час побудови?), Але зараз, я вважаю, що gcc просто намагається бути сумісними назад.
Лу Франко

3

Якщо я поклав stdlib.h або stdio.h, мені не доведеться посилати їх, але я маю посилатись, коли компілюю:

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

У тому числі math.h - це лише перший крок до отримання доступу до всіх математичних функцій.

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

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


2

stdio є частиною стандартної бібліотеки С, з якою за замовчуванням gcc буде посилатися на.

Реалізації математичної функції знаходяться в окремому файлі libm, який за замовчуванням не пов'язаний, тому вам потрібно вказати -lm. До речі, між тими заголовними файлами та бібліотечними файлами немає зв’язку.


3
він знає, що він питає, чому
Еван Теран

Він каже чому. Саймон пояснює, що деякі бібліотеки пов'язані за замовчуванням, як stdio, тоді як математична бібліотека не пов'язана за замовчуванням, тому її потрібно вказати.
mnuzzo

5
Я б сказав, що природа запитання полягає в тому, чому libm не пов'язана за замовчуванням (або навіть окремо від libc), оскільки його вміст значною мірою є частиною стандартної бібліотеки c.
Еван Теран

2

Я б здогадався, що це спосіб зробити додатки, які зовсім не використовують його, трохи ефективніше. Ось моя думка з цього приводу.

ОС x86 (і я думаю, що інші) повинні зберігати стан FPU на контекстному комутаторі. Однак більшість ОС намагаються зберегти / відновити цей стан після того, як програма намагається використовувати FPU вперше.

На додаток до цього, в математичній бібліотеці є певний базовий код, який встановить FPU у здоровий базовий стан при завантаженні бібліотеки.

Отже, якщо ви взагалі не зв’язуєте жоден математичний код, нічого з цього не відбудеться, тому ОС не повинна взагалі зберігати / відновлювати будь-який стан FPU, роблячи контекстні перемикання дещо ефективнішими.

Тільки здогадка, хоча.

EDIT: у відповідь на деякі коментарі, те саме базове передумови досі застосовується до випадків, що не належать до FPU (припущення полягає в тому, що програми, які не використовують libm, працюють трохи краще).

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

Крім того, якщо доступно лише статичне посилання, то застосовується аналогічний аргумент, який би зберігав виконувані розміри та час компіляції.


Якщо ви не зв’язуєтеся з libm, але доторкаєтесь до x87 FPU за допомогою інших засобів (наприклад, операції з плавцями), ядро ​​x86 потрібно зберегти стан FPU. Я не думаю, що це дуже гарна здогадка ...
ефемія

звичайно, якщо ви вручну використовуєте FPU, ядро ​​все одно буде потрібно зберегти / відновити його стан. Я говорив, що якщо ви ніколи не використовуєте його (в тому числі не використовуєте libm), тоді це не доведеться.
Еван Теран

Дійсно, це може дуже сильно залежати від ядра. Математична бібліотека, яку використовує ядро, може мати функцію save_FPU_on_switch (), яка вмикає її, а інші просто виявляють, чи торкнувся FPU.
Earlz

1
Якщо я правильно пам'ятаю, цілий випуск давно передує співпроцесорам з плаваючою точкою, навіть будучи на мікропроцесорах.
Носредна

@earlz: підхід із збереженням запиту математичної бібліотеки був би жахливим дизайном. Що робити, якщо вони використовують ФПУ якимись іншими способами? Єдиним розумним підходом (окрім просто завжди збереження / відновлення) було б виявлення використання та потім початок збереження / відновлення.
Еван Теран
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.