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


254

Se eu incluir <stdlib.h>ou <stdio.h>em um programa C, não preciso vinculá-los ao compilar, mas preciso vincular <math.h>, usando -lmcom o gcc, por exemplo:

gcc test.c -o test -lm

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

Respostas:


249

As funções stdlib.he stdio.htêm implementações em libc.so(ou libc.apara vinculação estática), que são vinculadas ao seu executável por padrão (como se -lcfossem especificadas). O GCC pode ser instruído a evitar esse link automático com as opções -nostdlibou -nodefaultlibs.

As funções matemáticas em math.htêm implementações em libm.so(ou libm.apara vinculação estática) e libmnão são vinculadas por padrão. Há razões históricas para este libm/ libcdivisão, nenhuma delas muito convincente.

Curiosamente, o tempo de execução C ++ libstdc++exige libm, portanto, se você compilar um programa C ++ com GCC ( g++), será automaticamente libmvinculado.


8
Isso não tem nada a ver com o Linux, pois era comum muito antes do Linux. Suspeito que isso tenha algo a ver com a tentativa de minimizar o tamanho do executável, pois existem muitos programas que não precisam de funções matemáticas.
David Thornley

39
Em sistemas antigos, se as funções matemáticas estivessem contidas na libc, a compilação de todos os programas seria mais lenta, os executáveis ​​de saída seriam maiores e o tempo de execução exigiria mais memória, sem benefício para a maioria dos programas que não usam essas funções matemáticas. Atualmente, temos um bom suporte para bibliotecas compartilhadas e, mesmo quando vinculadas estaticamente, as bibliotecas padrão são configuradas para que o código não utilizado possa ser descartado, portanto, nenhuma dessas são mais boas razões.
ephemient 23/06/09

38
@ephemient Mesmo nos velhos tempos, o link para uma biblioteca não puxava todo o conteúdo da biblioteca para o executável. Os vinculadores, embora sejam uma tecnologia frequentemente ignorada, historicamente têm sido bastante eficazes.

7
@ephemient Além disso, as bibliotecas compartilhadas existem há mais tempo do que você imagina. Eles foram inventados nos anos 50, não nos anos 80.

5
Suponho que, no final das contas, o que estamos vendo nada mais seja do que o conservadorismo do GCC: "sempre funcionou assim". Eu gostaria que eles aplicassem o mesmo raciocínio às suas extensões de compilador.

77

Lembre-se de que C é uma linguagem antiga e que FPUs são um fenômeno relativamente recente. Vi C pela primeira vez em processadores de 8 bits, onde era muito trabalhoso fazer aritmética de números inteiros de 32 bits. Muitas dessas implementações nem sequer tinham 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 complementos caros.

Para fazer toda essa matemática de ponto flutuante, você precisava de uma biblioteca bastante considerável. E a matemática seria lenta. Então você raramente usava carros alegóricos. Você tentou fazer tudo com números inteiros ou inteiros em escala. Quando você tinha que incluir math.h, rangia os dentes. Geralmente, você escreve suas próprias aproximações e tabelas de pesquisa para evitá-lo.

As trocas existiram por um longo tempo. Às vezes, havia pacotes matemáticos concorrentes chamados "fastmath" ou algo assim. 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 no computador que a maioria das implementações se tornou óbvia. Eu imagino que há algum programador por aí em algum lugar agora, trabalhando em um chip incorporado, tentando decidir se deve trazer 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 usasse um único flutuador. Se as FPUs sempre existissem e os carros alegóricos e duplos sempre fossem baratos para operar, sem dúvida haveria um "padrão".


Estou usando o Pade approximants para (1 + x) ^ y em Java, em um PC de mesa. Log, exp e pow ainda estão lentos.
quant_dev

Bom ponto. E vi aproximações para sin () em plugins de áudio.
Nosredna

11
Isso explica por que libmnão está vinculado por padrão, mas a matemática era padrão no C89 e, antes disso, a K&R havia de fato o padronizado, de modo que sua observação "stdmath" não faz sentido.
Fred Foo

@FredFoo Os tipos e interfaces foram padronizados, mas não as implementações. Eu acho que Nosredna está se referindo a uma biblioteca matemática padrão.
Tim Bird

72

Por causa da prática histórica ridícula que ninguém está disposto a consertar. A consolidação de todas as funções exigidas por C e POSIX em um único arquivo de biblioteca não apenas evitaria que essa pergunta fosse feita repetidamente, mas também economizaria uma quantidade significativa de tempo e memória ao vincular dinamicamente, pois cada .soarquivo vinculado requer as operações do sistema de arquivos para localizá-lo e localizá-lo, e algumas páginas para suas variáveis ​​estáticas, realocações etc.

Uma implementação em que todas as funções estão em uma biblioteca e o -lm, -lpthread, -lrt, etc. opções são todos os não-ops (ou link para vazios .aarquivos) está perfeitamente conformant POSIX e certamente preferível.

Nota: Estou falando do POSIX porque o próprio C não especifica nada sobre como o compilador é chamado. Portanto, você pode apenas tratar gcc -std=c99 -lmcomo a maneira específica de implementação em que o compilador deve ser chamado para um comportamento compatível.


9
+1 por apontar que o POSIX não exige a existência de bibliotecas separadas libm, libc e librt. Como exemplo, no Mac OS, tudo está localizado em um único libSystem (que também inclui libdbm, libdl, libgcc_s, libinfo, libm, libpoll, libproc e librpcsvc).
F'x

3
–1 para especular sobre o impacto da pesquisa da biblioteca no desempenho sem fazer backup com um link ou números. "Perfil. Não especule"
F'x 5/01/11

12
Isso não é especulação. Não tenho artigos publicados, mas eu mesmo fiz todas as medições e a diferença é enorme. Basta usar stracecom uma das opções de tempo para observar quanto tempo de inicialização é gasto na vinculação dinâmica ou comparar a execução ./configureem um sistema em que todos os utilitários padrão estão vinculados à estática versus um em que eles estão vinculados à dinâmica. Até os principais desenvolvedores de aplicativos de desktop e integradores de sistemas estão cientes dos custos da vinculação dinâmica; é por isso que existem coisas como pré-ligação. Tenho certeza de que você pode encontrar referências em alguns desses documentos.
R .. GitHub Pare de ajudar o gelo

1
Note-se que POSIX se exigem -lmpara ser aceito e aplicações que usam as interfaces de matemática deve usar -lm, mas pode ser uma opção interna tratada (ou mesmo ignorado) pelo comando do compilador, e não um arquivo de biblioteca real. Ou pode ser apenas um .aarquivo vazio se as interfaces estiverem na libc principal.
R .. GitHub Pare de ajudar o gelo

6
@ FX: Não sei por que eu esqueci de mencionar isso antes: strace -ttmostrará facilmente o tempo gasto na vinculação dinâmica. Não é bonito. E no Linux, a inspeção /proc/sys/smapsmostrará a sobrecarga de memória de bibliotecas adicionais.
R .. GitHub Pare de ajudar o gelo

33

Porque time()e algumas outras funções são builtindefinidas na própria biblioteca C ( libc) e o GCC sempre se vincula à libc, a menos que você use a -ffreestandingopção de compilação. No entanto, existem funções matemáticas nas libmquais não está implicitamente vinculado pelo gcc.


8
No LLVM gcc, não preciso adicionar -lm. Por que é isso?
Bot47

26

Uma explicação é dada aqui :

Portanto, se o seu programa estiver usando funções matemáticas e inclusive math.h, será necessário vincular explicitamente a biblioteca matemática passando a -lmbandeira. A razão dessa separação específica é que os matemáticos são muito exigentes quanto à maneira 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 libc.a, não seria possível fazer isso.

[Editar]

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


10
Acho que não há garantia de que isso aconteça; você pode acabar com um conflito de símbolos. Provavelmente dependeria do vinculador e do layout da biblioteca. Eu ainda acho essa razão fraca; se você estiver fazendo uma função sqrt personalizado, você realmente não deve dar-lhe o mesmo nome que a função sqrt padrão, mesmo que faz a mesma coisa ...
ephemient

1
De fato, criar sua própria função (não estática) nomeada sqrtresulta em um programa com comportamento indefinido.
R .. GitHub Pare de ajudar o gelo

@Bastien Boa localização. E chegando ao seu ponto, o que você quer dizer com "antes da biblioteca padrão"? Eu pensei que a biblioteca padrão está vinculada por padrão e não precisa ser vinculada por meio de opções de linha de comando. Portanto, a biblioteca padrão será a primeira opção para o vinculador e não será possível colocar sua própria implementação "antes da biblioteca padrão".
Rocky Inde

@RockyInde: olhe minha resposta, acho que realmente quis dizer "antes da biblioteca matemática padrão". Mas acho que existem opções do compilador para não vincular a biblioteca C padrão, o que permitiria que você passasse a sua.
Bastien Léonard

@ BastienLéonard Eu uso o gcc da versão 7.2, que -lmé totalmente opcional. Alguma idéia
Donghua Liu

5

Há uma discussão aprofundada sobre como vincular a bibliotecas externas em Uma introdução ao GCC - Vinculando a bibliotecas externas . Se uma biblioteca é membro das bibliotecas padrão (como stdio), não é necessário especificar ao 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 ele vincula a ambos têm muito a dizer sobre por que os dois são separados.

Observe que muitas das funções em 'libm.a' (a biblioteca matemática) são definidas em 'math.h', mas não estão presentes na libc.a. Alguns são, o que pode ser confuso, mas a regra geral é esta - a biblioteca C contém as funções que o ANSI determina que devem existir, para que você não precise do -lm se usar apenas as funções do ANSI. Por outro lado, `libm.a 'contém mais funções e suporta funcionalidades adicionais, como retorno de chamada do matherr e conformidade com vários padrões alternativos de comportamento em caso de erros de FP. Veja a seção libm, para mais detalhes.


1
O que não responde à pergunta de por que você deve vincular as bibliotecas de correspondências separadamente. Obviamente, você deseja vincular bibliotecas OpenGL separadamente, mas sem dúvida as bibliotecas matemáticas são geralmente úteis.
David Thornley

@ David: Você está certo. Não estava claro para mim pela pergunta que esse era o assunto que o OP estava perguntando. Eu estava editando minha resposta como você comentou.
Bill o Lagarto

Eu sei o motivo pelo qual compilei um programa que usa a sqrtfunção e funciona sem incluir a biblioteca via -lm. Obrigado!
L_K

5

Como disse o ephemiente, a biblioteca C libc está vinculada por padrão e esta biblioteca contém as implementações de stdlib.h, stdio.h e vários outros arquivos de cabeçalho padrão. Apenas para acrescentar, 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 vincula a biblioteca C.


3

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

Dá a você a oportunidade de substituí-lo por um diferente, com as mesmas funções, mas não acho 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 sobre por que o cc faz isso é por causa do tempo de construção - o cc foi escrito para máquinas com muito menos energia do que temos agora. Muitos programas não possuem matemática de ponto flutuante e provavelmente usaram todas as bibliotecas que não eram comumente usadas fora do padrão. Suponho que o tempo de construção do SO UNIX e as ferramentas que o acompanham foram a força motriz.


Eu acho que a mentalidade por trás da pergunta é que o conteúdo do libm faz parte da biblioteca C padrão, por que eles não estão no libc?
Evan Teran

1
O motivo para o gcc é manter a compatibilidade com o cc original no AT&T Unix. Eu usei 3B2s em 1988 e você tinha que -lm para obter matemática. Pareceu-me completamente arbitrário na época. No Visual Studio, não me lembro de ter que adicionar matemática, mas às vezes é necessário adicionar outras bibliotecas aparentemente em c-runtime. Presumo que os fornecedores do compilador tenham um motivo (tempo de compilação?), Mas, no momento, aposto que o gcc está apenas tentando ser compatível com versões anteriores.
23609 Lou Franco

3

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

stdlib.h, stdio.hsão os arquivos de cabeçalho. 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, é aí que as funções realmente vivem.

A inclusão math.hé apenas o primeiro passo para obter acesso a todas as funções matemáticas.

Além disso, você não precisa se vincular libmse não usar suas funções, mesmo que faça uma #include <math.h>etapa apenas informativa para o compilador sobre os símbolos.

stdlib.h, stdio.hconsulte as funções disponíveis em libc, que sempre estão vinculadas para que o usuário não precise fazer isso sozinho.


2

O stdio faz parte da biblioteca C padrão com a qual, por padrão, o gcc se vincula.

As implementações da função matemática estão em um arquivo libm separado que não está vinculado por padrão, portanto, você deve especificá-lo -lm. A propósito, não há relação entre esses arquivos de cabeçalho e os arquivos de biblioteca.


3
ele sabe that..he está pedindo por isso
Evan Teran

Ele diz o porquê. Simon explica que algumas bibliotecas estão vinculadas por padrão, como stdio, enquanto a biblioteca matemática não está vinculada por padrão, portanto, ela deve ser especificada.
Mnuzzo

5
Eu diria que a natureza da pergunta é perguntar por que o libm não está vinculado por padrão (ou mesmo separado do libc), pois seu conteúdo faz parte da biblioteca padrão c.
Evan Teran

2

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

Os sistemas operacionais x86 (e imagino que outros) precisam armazenar o estado da FPU na alternância de contexto. No entanto, a maioria dos sistemas operacionais apenas se preocupa em salvar / restaurar esse estado após o aplicativo tentar usar o FPU pela primeira vez.

Além disso, provavelmente há algum código básico na biblioteca matemática que definirá a FPU para um estado de base saudável 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 precisará salvar / restaurar nenhum estado da FPU, tornando as alternâncias de contexto um pouco mais eficientes.

Apenas um palpite.

EDIT: em resposta a alguns dos comentários, a mesma premissa básica ainda se aplica a casos que não são da FPU (a premissa é que era para fazer aplicativos que não usassem libm ter um desempenho um pouco melhor).

Por exemplo, se houver uma FPU flexível que era provável nos primeiros dias de C. A separação da libm poderia impedir que muitos códigos grandes (e lentos, se usados) fossem desnecessariamente vinculados.

Além disso, se houver apenas vinculação estática disponível, um argumento semelhante se aplica para manter os tamanhos dos executáveis ​​e diminuir o tempo de compilação.


Se você não vincular à libm, mas tocar na FPU x87 por outros meios (operações em carros alegóricos, por exemplo), o kernel x86 precisa salvar o estado da FPU. Eu não acho que este é um muito bom palpite ...
ephemient

é claro que se você usar manualmente a FPU, o kernel ainda precisará salvar / restaurar seu estado. Eu estava dizendo que se você nunca usá-lo (incluindo não usar o libm), ele não precisará.
Evan Teran

Realmente, pode depender muito do kernel. A biblioteca matemática usada pelo kernel pode ter uma função save_FPU_on_switch () que a ativa, enquanto outros apenas detectam se a FPU foi tocada.
23609 Earlz

1
Se bem me lembro, toda a questão é anterior aos coprocessadores de ponto flutuante, mesmo estando em microprocessadores.
Nosredna

@earlz: a abordagem de salvar a solicitação da biblioteca de matemática seria um projeto terrível. E se eles usarem a FPU por outros meios? A única abordagem sensata (além de sempre salvar / restaurar) seria detectar o uso e começar a salvar / restaurar.
Evan Teran
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.