Odpowiedzi:
Funkcje w stdlib.h
i stdio.h
mają implementacje w libc.so
(lub libc.a
do łączenia statycznego), które są domyślnie połączone z plikiem wykonywalnym (tak jakby -lc
zostały określone). GCC można poinstruować, aby unikał tego automatycznego połączenia z opcjami -nostdlib
lub -nodefaultlibs
.
Funkcje matematyczne math.h
mają implementacje w libm.so
(lub libm.a
do łączenia statycznego) i libm
domyślnie nie są połączone. Istnieją historyczne powody tego libm
/ libc
podziału, żaden z nich nie jest zbyt przekonujący.
Co ciekawe, środowisko wykonawcze C ++ libstdc++
wymaga libm
, więc jeśli skompilujesz program C ++ za pomocą GCC ( g++
), automatycznie zostaniesz libm
połączony.
Pamiętaj, że C jest starym językiem i że FPU są stosunkowo nowym zjawiskiem. Po raz pierwszy zobaczyłem C na 8-bitowych procesorach, w których dużo pracy zajmowała nawet arytmetyka liczb całkowitych 32-bitowych. Wiele z tych wdrożeń nawet nie mieć pływający punkt biblioteka matematyczna dostępny!
Nawet na pierwszych 68000 maszynach (Mac, Atari ST, Amiga) koprocesory zmiennoprzecinkowe były często drogimi dodatkami.
Aby wykonać całą tę matematykę zmiennoprzecinkową, potrzebna była dość spora biblioteka. A matematyka będzie wolna. Więc rzadko używałeś pływaków. Próbowałeś zrobić wszystko za pomocą liczb całkowitych lub skalowanych liczb całkowitych. Kiedy musiałeś uwzględnić matematykę, zgrzytałeś zębami. Często piszesz własne przybliżenia i tabele wyszukiwania, aby tego uniknąć.
Kompromisy istniały przez długi czas. Czasami istniały konkurencyjne pakiety matematyczne o nazwie „fastmath” lub podobne. Jakie jest najlepsze rozwiązanie matematyczne? Naprawdę dokładne, ale powolne rzeczy? Niedokładne, ale szybkie? Duże tabele dla funkcji trig? Większość implementacji stała się oczywista dopiero po zagwarantowaniu, że koprocesory znajdują się w komputerze. Wyobrażam sobie, że jest gdzieś jakiś programista, pracujący na wbudowanym układzie scalonym, próbujący zdecydować, czy wprowadzić bibliotekę matematyczną, aby poradzić sobie z jakimś problemem matematycznym.
Właśnie dlatego matematyka nie była standardem . Wiele, a może większość programów nie używało pojedynczej liczby zmiennoprzecinkowej. Gdyby FPU zawsze były w pobliżu, a operacje zmiennoprzecinkowe i podwajania zawsze były tanie w obsłudze, bez wątpienia byłby to „standard”.
libm
domyślnie nie jest połączone, ale matematyka była standardowa od C89, a wcześniej K&R de facto ją ustandaryzował, więc twoja uwaga „stdmath” nie ma sensu.
Z powodu absurdalnej praktyki historycznej, której nikt nie chce naprawić. Skonsolidowanie wszystkich funkcji wymaganych przez C i POSIX w jednym pliku biblioteki nie tylko pozwoliłoby uniknąć wielokrotnego zadawania tego pytania, ale także zaoszczędziłoby znaczną ilość czasu i pamięci podczas dynamicznego łączenia, ponieważ każdy .so
połączony plik wymaga operacji systemu plików zlokalizować i znaleźć, a także kilka stron dla jego zmiennych statycznych, relokacji itp.
Implementacja gdzie wszystkie funkcje są w jednej biblioteki i -lm
, -lpthread
, -lrt
, itd opcje są wszystkie no-ops (lub link do pustych .a
plików) jest idealnie zgodnym z POSIX i na pewno korzystne.
Uwaga: mówię o POSIX, ponieważ samo C nie określa niczego, jak wywoływany jest kompilator. W ten sposób można traktować gcc -std=c99 -lm
jako sposób specyficzny dla implementacji, że kompilator musi być wywoływany w celu zachowania zgodności.
strace
jednej z opcji pomiaru czasu, aby zobaczyć, ile czasu startowego spędza się na łączeniu dynamicznym, lub porównać działanie ./configure
w systemie, w którym wszystkie standardowe narzędzia są połączone statycznie z tymi, w których są one połączone dynamicznie. Nawet główni twórcy aplikacji komputerowych i integratorzy systemów są świadomi kosztów dynamicznego łączenia; dlatego istnieją rzeczy takie jak prelink. Jestem pewien, że możesz znaleźć wzorce w niektórych z tych artykułów.
-lm
, aby być przyjęte i aplikacji, które wykorzystują interfejsy matematycznych musi używać -lm
, ale może to być opcja wewnętrzny obchodzić (lub nawet ignorowane) przez polecenie kompilatora, a nie rzeczywiste plik biblioteki. Lub może to być pusty .a
plik, jeśli interfejsy znajdują się w głównym libc.
strace -tt
łatwo pokażę ci czas poświęcony na dynamiczne linkowanie. To nie jest ładne. A w Linuksie inspekcja /proc/sys/smaps
pokaże ci narzut pamięci dodatkowych bibliotek.
Ponieważ time()
i niektóre inne funkcje są builtin
zdefiniowane w samej bibliotece C ( libc
), a GCC zawsze prowadzi do libc, chyba że użyjesz -ffreestanding
opcji kompilacji. Jednak żyją funkcje matematyczne, w libm
których gcc nie jest pośrednio powiązany.
Wyjaśnienie podano tutaj :
Więc jeśli twój program korzysta z funkcji matematycznych i włącznie
math.h
, musisz jawnie połączyć bibliotekę matematyczną, przekazując-lm
flagę. Powodem tego szczególnego rozdzielenia jest to, że matematycy są bardzo wybredni w zakresie sposobu obliczania ich matematyki i mogą chcieć użyć własnej implementacji funkcji matematycznych zamiast standardowej implementacji. Gdyby funkcje matematyczne były skupione,libc.a
nie byłoby to możliwe.
[Edytować]
Nie jestem jednak pewien, czy się z tym zgadzam. Jeśli masz bibliotekę, która zapewnia, powiedzmy, sqrt()
i przekazujesz ją przed biblioteką standardową, linker uniksowy przejmie twoją wersję, prawda?
sqrt
powoduje, że program ma niezdefiniowane zachowanie.
-lm
jest całkowicie opcjonalna. Wszelkie pomysły
Dokładna dyskusja na temat linkowania do bibliotek zewnętrznych znajduje się we wstępie do GCC - Linkowanie do bibliotek zewnętrznych . Jeśli biblioteka jest członkiem standardowych bibliotek (takich jak stdio), nie trzeba określać w kompilatorze (tak naprawdę linkerze), aby je połączyć.
EDYCJA: Po przeczytaniu niektórych innych odpowiedzi i komentarzy, myślę, że odwołanie do libc.a i odwołanie do libm, które łączy do obu, mają wiele do powiedzenia na temat tego, dlaczego oba są oddzielne.
Zauważ, że wiele funkcji w „libm.a” (biblioteka matematyczna) jest zdefiniowanych w „math.h”, ale nie ma ich w libc.a. Niektóre z nich mogą być mylące, ale ogólna zasada jest taka - biblioteka C zawiera funkcje, które ANSI nakazuje istnieć, więc nie potrzebujesz -lm, jeśli używasz tylko funkcji ANSI. W przeciwieństwie do tego, `libm.a 'zawiera więcej funkcji i obsługuje dodatkowe funkcje, takie jak oddzwanianie matherr i zgodność z kilkoma alternatywnymi standardami zachowania w przypadku błędów FP. Zobacz sekcję libm, aby uzyskać więcej informacji.
sqrt
funkcji i działa bez włączania biblioteki przez -lm
. Dzięki!
Jak powiedział ephemient, biblioteka libc w C jest domyślnie połączona i ta biblioteka zawiera implementacje stdlib.h, stdio.h i kilka innych standardowych plików nagłówkowych. Aby dodać do tego, zgodnie z „ An Introduction to GCC ”, polecenie linkera dla podstawowego programu „Hello World” w C wygląda następująco:
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
Zwróć uwagę na opcję -lc w trzecim wierszu, który łączy bibliotekę C.
Myślę, że to trochę arbitralne. Musisz gdzieś narysować linię (które biblioteki są domyślne, a które należy określić).
Daje to możliwość zastąpienia go innym, który ma te same funkcje, ale nie sądzę, że jest to bardzo powszechne.
EDYCJA: (z moich własnych komentarzy): Myślę, że gcc robi to, aby zachować kompatybilność wsteczną z oryginalnym cc. Domyślam się, dlaczego cc robi to z powodu czasu kompilacji - cc zostało napisane dla maszyn o znacznie mniejszej mocy niż obecnie. Wiele programów nie ma matematyki zmiennoprzecinkowej i prawdopodobnie wzięły każdą bibliotekę, która nie była często używana jako domyślna. Zgaduję, że siłą napędową był czas kompilacji systemu operacyjnego UNIX i towarzyszące mu narzędzia.
Jeśli wstawię stdlib.h lub stdio.h, nie muszę ich łączyć, ale muszę je łączyć podczas kompilacji:
stdlib.h
, stdio.h
to pliki nagłówkowe. Uwzględniasz je dla swojej wygody. Prognozują tylko, jakie symbole staną się dostępne, jeśli umieścisz link w odpowiedniej bibliotece. Implementacje znajdują się w plikach biblioteki, tam właśnie naprawdę działają te funkcje.
Uwzględnienie math.h
to tylko pierwszy krok do uzyskania dostępu do wszystkich funkcji matematycznych.
Ponadto nie musisz tworzyć odnośników, libm
jeśli nie używasz jego funkcji, nawet jeśli robisz to, #include <math.h>
co jest tylko informacyjnym krokiem dla kompilatora o symbolach.
stdlib.h
, stdio.h
odnoszą się do funkcji dostępnych w libc
, które są zawsze połączone, aby użytkownik nie musiał tego robić sam.
stdio jest częścią standardowej biblioteki C, z którą domyślnie łączy się gcc.
Implementacje funkcji matematycznych znajdują się w osobnym pliku libm, do którego domyślnie nie jest podłączony, więc należy go podać -lm. Nawiasem mówiąc, nie ma związku między tymi plikami nagłówkowymi a plikami bibliotek.
Ja przypuszczam , że jest to sposób, aby aplikacje, które nie używać go w ogóle wykonać nieco lepiej. Oto moje przemyślenie na ten temat.
Systemy operacyjne x86 (i wyobrażam sobie inne) muszą przechowywać stan FPU na przełączniku kontekstu. Jednak większość systemów operacyjnych zadaje sobie trud tylko zapisać / przywrócić ten stan po pierwszej próbie użycia FPU przez aplikację.
Oprócz tego w bibliotece matematycznej znajduje się prawdopodobnie jakiś podstawowy kod, który ustawi FPU na zdrowy stan podstawowy po załadowaniu biblioteki.
Tak więc, jeśli w ogóle nie użyjesz żadnego kodu matematycznego, nic takiego się nie wydarzy, dlatego system operacyjny nie musi w ogóle zapisywać / przywracać żadnego stanu FPU, dzięki czemu przełączanie kontekstu jest nieco bardziej wydajne.
Tylko zgadnij.
EDYTOWAĆ: w odpowiedzi na niektóre komentarze ta sama podstawowa przesłanka nadal ma zastosowanie w przypadkach innych niż FPU (założeniem jest, że aplikacje, które nie wykorzystywały libm, działają nieco lepiej).
Na przykład, jeśli istnieje soft-FPU, który był podobny we wczesnych dniach C. Wtedy oddzielne libm może zapobiec niepotrzebnemu łączeniu się dużego (i powolnego, jeśli był użyty) kodu.
Ponadto, jeśli dostępne jest tylko statyczne łączenie, stosuje się podobny argument, że utrzyma on rozmiary plików wykonywalnych i skróci czas kompilacji.