Por que você tem que vincular a biblioteca de matemática em C?

Se eu include ou em um programa em C, não preciso vinculá-los ao compilar, mas preciso vincular a , usando -lm com gcc, por exemplo:

 gcc test.c -o test -lm 

Qual é a razão para isto? Por que preciso vincular explicitamente a biblioteca de matemática, mas não as outras?

    As funções em stdlib.h e stdio.h têm implementações em libc.so (ou libc.a para link estático), que é vinculado ao seu executável por padrão (como se -lc fosse especificado). O GCC pode ser instruído para evitar esse link automático com as opções -nodefaultlibs ou -nodefaultlibs .

    As funções matemáticas em math.h têm implementações em libm.so (ou libm.a para vinculação estática) e a libm não está vinculada por padrão. Existem razões históricas para essa libm / libc , nenhuma delas muito convincente.

    Curiosamente, o libstdc++ ++ de tempo de execução do C ++ requer o libm , portanto, se você compilar um programa C ++ com o GCC ( g++ ), você receberá automaticamente o libm vinculado.

    Lembre-se de que C é uma linguagem antiga e que as FPUs são um fenômeno relativamente recente. Eu vi pela primeira vez C em processadores de 8 bits, onde era muito trabalhoso fazer aritmética inteira de 32 bits. Muitas dessas implementações nem sequer têm uma biblioteca de matemática de ponto flutuante disponível!

    Mesmo nas primeiras 68000 máquinas (Mac, Atari ST, Amiga), os coprocessadores de ponto flutuante eram frequentemente caros add-ons.

    Para fazer toda essa matemática de ponto flutuante, você precisava de uma biblioteca bem grande. E a matemática ia ser lenta. Então você raramente usava carros alegóricos. Você tentou fazer tudo com números inteiros ou inteiros escalados. Quando você teve que include a matemática, você cerrou os dentes. Muitas vezes, você escreveria suas próprias aproximações e tabelas de pesquisa para evitá-lo.

    Trade-offs existe há muito tempo. Às vezes, havia pacotes matemáticos concorrentes chamados “fastmath” ou algo parecido. Qual a melhor solução para matemática? Coisas realmente precisas, mas lentas? Impreciso mas rápido? Tabelas grandes para funções trigonométricas? Não foi até que os coprocessadores estivessem garantidos no computador que a maioria das implementações se tornou óbvia. Eu imagino que há algum programador em algum lugar agora, trabalhando em um chip embutido, tentando decidir se traz a biblioteca de matemática para lidar com algum problema de matemática.

    É por isso que a matemática não era padrão . Muitos ou talvez a maioria dos programas não usou um único float. Se as FPUs sempre existissem e os carros alegóricos e os duplos fossem sempre baratos para operar, sem dúvida haveria um “stdmath”.

    Uma explicação é dada aqui :

    Portanto, se o seu programa estiver usando funções matemáticas e incluindo math.h , você precisará vincular explicitamente a biblioteca matemática passando o sinalizador -lm . A razão para essa separação em particular é que os matemáticos são muito exigentes quanto à forma como sua matemática está sendo computada e podem querer usar sua própria implementação das funções matemáticas em vez da implementação padrão. Se as funções matemáticas fossem agrupadas em libc.a , não seria possível fazer isso.

    [Editar]

    Eu não tenho certeza se eu concordo com isso, no entanto. Se você tem uma biblioteca que fornece, digamos, sqrt() , e você a passa antes da biblioteca padrão, um vinculador Unix levará sua versão, certo?

    Como disse o ephemient, a libc da biblioteca C é vinculada por padrão e essa biblioteca contém as implementações de stdlib.h, stdio.h e vários outros arquivos de header padrão. Apenas para adicionar a ele, de acordo com ” An Introduction to GCC “, o comando linker para um programa básico “Hello World” em C é o seguinte:

     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 

    Observe a opção -lc na terceira linha que liga a biblioteca C.

    Há uma discussão completa de vinculação a bibliotecas externas em Introdução ao GCC – Vinculando a bibliotecas externas . Se uma biblioteca é um membro das bibliotecas padrão (como stdio), então você não precisa especificar para o compilador (realmente o vinculador) para vinculá-las.

    EDIT: Depois de ler algumas das outras respostas e comentários, acho que a referência libc.a e a referência libm que liga a ambos têm muito a dizer sobre por que os dois são separados.

    Note que muitas das funções em ‘libm.a’ (a biblioteca matemática) são definidas em ‘math.h’, mas não estão presentes em libc.a. Alguns são, o que pode ser confuso, mas a regra básica é essa – a biblioteca C contém aquelas funções que o ANSI dita devem existir, para que você não precise do -lm se você usar apenas funções ANSI. Em contraste, o `libm.a ‘contém mais funções e suporta funcionalidades adicionais, tais como o callback de matherr e a conformidade com vários padrões alternativos de comportamento no caso de erros FP. Veja a seção libm, para mais detalhes.

    O stdio é parte da biblioteca C padrão que, por padrão, o gcc irá vincular.

    As implementações da function matemática estão em um arquivo libm separado que não está vinculado por padrão, portanto, você deve especificá-lo -lm. By the way, não há relação entre os arquivos de header e arquivos de biblioteca.

    Eu acho que é meio arbitrário. Você tem que desenhar uma linha em algum lugar (quais bibliotecas são padrão e quais precisam ser especificadas).

    Isso lhe dá a oportunidade de substituí-lo por um diferente que tenha as mesmas funções, mas não acho que seja muito comum fazê-lo.

    EDIT: (dos meus próprios comentários): Eu acho que o gcc faz isso para manter a compatibilidade com o cc original. Meu palpite para o porque cc faz isso é por causa do tempo de compilation – cc foi escrito para máquinas com muito menos energia do que temos agora. Muitos programas não têm matemática de ponto flutuante e provavelmente pegaram todas as bibliotecas que não eram comumente usadas fora do padrão. Eu estou supondo que o tempo de construção do sistema operacional UNIX e as ferramentas que o acompanham foram a força motriz.

    Se eu colocar stdlib.h ou stdio.h, não preciso vinculá-los, mas preciso vincular quando compilar:

    stdlib.h , stdio.h são os arquivos de header. Você os inclui para sua conveniência. Eles apenas prevêem quais símbolos ficarão disponíveis se você vincular na biblioteca adequada. As implementações estão nos arquivos da biblioteca, é onde as funções realmente vivem.

    Incluindo math.h é apenas o primeiro passo para obter access a todas as funções matemáticas.

    Além disso, você não precisa se ligar ao libm se você não usar suas funções, mesmo se você fizer um #include

    que é apenas uma etapa informativa para você, para o compilador sobre os símbolos.

    stdlib.h , stdio.h referem-se a funções disponíveis no libc , que por acaso estão sempre ligadas para que o usuário não tenha que fazê-lo sozinho.

    Eu diria que é uma maneira de fazer com que os aplicativos que não o usam tenham um desempenho um pouco melhor. Aqui está o meu pensamento sobre isso.

    Os sistemas operacionais x86 (e imagino outros) precisam armazenar o estado FPU no comutador de contexto. No entanto, a maioria dos sistemas operacionais só se preocupa em salvar / restaurar esse estado depois que o aplicativo tenta usar o FPU pela primeira vez.

    Além disso, provavelmente existe algum código básico na biblioteca de matemática que definirá o FPU para um estado básico sã quando a biblioteca for carregada.

    Portanto, se você não vincular nenhum código matemático, nada disso acontecerá, portanto, o sistema operacional não precisa salvar / restaurar nenhum estado de FPU, tornando os comutadores de contexto um pouco mais eficientes.

    Apenas um palpite embora.

    EDIT: em resposta a alguns dos comentários, a mesma premissa base ainda se aplica a casos não-FPU (a premissa é que era fazer aplicativos que não fizeram uso do libm executar um pouco melhor).

    Por exemplo, se houver uma FPU soft que tenha sido likley nos primeiros dias de C. Então, ter o libm separado poderia evitar que um código grande (e lento se fosse usado) fosse desnecessariamente vinculado.

    Além disso, se houver apenas vinculação estática disponível, um argumento semelhante será aplicado para manter os tamanhos dos executáveis ​​e os tempos de compilation inativos.