¿Por qué tienes que vincular la biblioteca matemática en C?


254

Si incluyo <stdlib.h>o <stdio.h>en un programa en C, no tengo que vincularlos al compilar, pero sí tengo que vincularlos <math.h>, usando -lmgcc, por ejemplo:

gcc test.c -o test -lm

¿Cuál es la razón para esto? ¿Por qué tengo que vincular explícitamente la biblioteca matemática pero no las otras bibliotecas?

Respuestas:


249

Las funciones en stdlib.hy stdio.htienen implementaciones en libc.so(o libc.apara enlace estático), que está vinculado a su ejecutable de forma predeterminada (como si -lcse especificara). Se puede indicar a GCC que evite este enlace automático con las opciones -nostdlibo -nodefaultlibs.

Las funciones matemáticas en math.htienen implementaciones en libm.so(o libm.apara enlaces estáticos), y libmno están vinculadas por defecto. Hay razones históricas para esto libm/ libcdivisión, ninguna de ellas muy convincente.

Curiosamente, el tiempo de ejecución de C ++ libstdc++requiere libm, por lo que si compila un programa de C ++ con GCC ( g++), se libmvinculará automáticamente .


8
Esto no tiene nada que ver con Linux, ya que era común mucho antes de Linux. Sospecho que tiene algo que ver con tratar de minimizar el tamaño del ejecutable, ya que hay muchos programas que no necesitan funciones matemáticas.
David Thornley

39
En sistemas antiguos, si las funciones matemáticas estuvieran contenidas en libc, la compilación de todos los programas sería más lenta, los ejecutables de salida serían más grandes y el tiempo de ejecución requeriría más memoria, sin beneficio para la mayoría de los programas que no utilizan estas funciones matemáticas en absoluto. En estos días tenemos un buen soporte para las bibliotecas compartidas, e incluso cuando se vinculan estáticamente, las bibliotecas estándar están configuradas para que el código no utilizado se pueda descartar, por lo que ninguna de estas son buenas razones.
ephemient

38
@ephemient Incluso en los viejos tiempos, el enlace a una biblioteca no incluía todos los contenidos de la biblioteca en el ejecutable. Los enlazadores, aunque una tecnología a menudo ignorada, históricamente han sido bastante eficientes.

77
@ephemient Además, las bibliotecas compartidas han existido por más tiempo de lo que piensas. Fueron inventados en la década de 1950, no en la de 1980.

55
Supongo que al final del día lo que estamos viendo no es más que conservadurismo del CCG: "siempre ha funcionado así". Solo desearía que aplicaran el mismo razonamiento a sus extensiones de compilador.

77

Recuerde que C es un lenguaje antiguo y que las FPU son un fenómeno relativamente reciente. La primera vez que vi C en procesadores de 8 bits fue mucho trabajo hacer incluso aritmética de enteros de 32 bits. ¡Muchas de estas implementaciones ni siquiera tenían una biblioteca matemática de punto flotante disponible!

Incluso en las primeras máquinas 68000 (Mac, Atari ST, Amiga), los coprocesadores de coma flotante a menudo eran complementos caros.

Para hacer todos esos cálculos de coma flotante, necesitabas una biblioteca bastante considerable. Y las matemáticas iban a ser lentas. Así que rara vez usabas flotadores. Intentaste hacer todo con enteros o enteros escalados. Cuando tenías que incluir matemáticas.h, apretabas los dientes. A menudo, escribirías tus propias aproximaciones y tablas de búsqueda para evitarlo.

Las compensaciones existieron durante mucho tiempo. A veces había paquetes matemáticos competitivos llamados "fastmath" o algo así. ¿Cuál es la mejor solución para las matemáticas? ¿Cosas realmente precisas pero lentas? ¿Inexacto pero rápido? Tablas grandes para funciones trigonométricas? No fue hasta que se garantizó que los coprocesadores estaban en la computadora que la mayoría de las implementaciones se hicieron evidentes. Me imagino que hay algún programador en algún lugar en este momento, trabajando en un chip incorporado, tratando de decidir si traer la biblioteca matemática para manejar algún problema matemático.

Por eso las matemáticas no eran estándar . Muchos o tal vez la mayoría de los programas no usaron un solo flotador. Si las FPU siempre hubieran existido y los flotadores y los dobles fueran siempre baratos para operar, sin duda habría habido una "mala educación".


Heh, estoy usando aproximaciones de Pade para (1 + x) ^ y en Java, en una PC de escritorio. Log, exp y pow todavía son lentos.
quant_dev

Buen punto. Y he visto aproximaciones para sin () en complementos de audio.
Nosredna

11
Esto explica por qué libmno está vinculado por defecto, pero las matemáticas eran estándar desde C89 y antes de eso, K&R lo había estandarizado de facto , por lo que su comentario "stdmath" no tiene sentido.
Fred Foo

@FredFoo Los tipos e interfaces se estandarizaron, pero no las implementaciones. Creo que Nosredna se refiere a una biblioteca matemática estándar.
Tim Bird

72

Debido a la práctica histórica ridícula que nadie está dispuesto a arreglar. La consolidación de todas las funciones requeridas por C y POSIX en un solo archivo de biblioteca no solo evitaría que esta pregunta se hiciera una y otra vez, sino que también ahorraría una cantidad significativa de tiempo y memoria al vincular dinámicamente, ya que cada .soarchivo vinculado requiere las operaciones del sistema de archivos para localizarlo y encontrarlo, y algunas páginas para sus variables estáticas, reubicaciones, etc.

Una aplicación que todas las funciones están en una biblioteca y el -lm, -lpthread, -lrt, etc. opciones son todos los no-ops (o enlace a vacíos .aarchivos) está perfectamente conformes con POSIX y ciertamente preferible.

Nota: estoy hablando de POSIX porque C en sí no especifica nada sobre cómo se invoca el compilador. Por lo tanto, puede tratar gcc -std=c99 -lmcomo la forma específica de implementación que se debe invocar al compilador para un comportamiento conforme.


99
+1 para señalar que POSIX no requiere que existan bibliotecas separadas libm, libc y librt. Como ejemplo, en Mac OS todo se encuentra en un solo libSystem (que también incluye libdbm, libdl, libgcc_s, libinfo, libm, libpoll, libproc y librpcsvc).
F'x

3
–1 por especular sobre el impacto de la búsqueda de la biblioteca en el rendimiento sin hacer una copia de seguridad con un enlace o números. "Perfil. No especules"
F'x

12
Esto no es especulación. No tengo documentos publicados, pero yo mismo he hecho todas las mediciones y la diferencia es enorme. Simplemente use stracecon una de las opciones de tiempo para ver cuánto tiempo de inicio se dedica a la vinculación dinámica, o compare la ejecución ./configureen un sistema donde todas las utilidades estándar están vinculadas estáticamente versus una donde están vinculadas dinámicamente. Incluso los principales desarrolladores de aplicaciones de escritorio e integradores de sistemas son conscientes de los costos de la vinculación dinámica; Por eso existen cosas como el preenlace. Estoy seguro de que puede encontrar puntos de referencia en algunos de esos documentos.
R .. GitHub DEJA DE AYUDAR A ICE

1
Tenga en cuenta que POSIX no requiere -lmque se acepte y las aplicaciones que utilizan las interfaces de matemáticas debe utilizar -lm, pero puede ser una opción interna manejada (o incluso ignorado) por el comando del compilador, no un archivo de biblioteca real. O simplemente puede ser un .aarchivo vacío si las interfaces están en la biblioteca principal.
R .. GitHub DEJA DE AYUDAR A ICE

66
@FX: No sé por qué olvidé mencionar esto antes: strace -ttle mostrará fácilmente el tiempo dedicado a la vinculación dinámica. No es lindo. Y en Linux, la inspección /proc/sys/smapsle mostrará la sobrecarga de memoria de bibliotecas adicionales.
R .. GitHub DEJA DE AYUDAR A HIELO

33

Porque time()y algunas otras funciones están builtindefinidas en la biblioteca C ( libc) y GCC siempre se vincula a libc a menos que use la -ffreestandingopción de compilación. Sin embargo, las funciones matemáticas viven en las libmque no está implícitamente vinculado por gcc.


8
En LLVM gcc no tengo que agregar -lm. ¿Por qué es esto?
bot47

26

Aquí se da una explicación :

Entonces, si su programa está utilizando funciones matemáticas e incluidas math.h, entonces necesita vincular explícitamente la biblioteca matemática pasando la -lmbandera. La razón de esta separación particular es que los matemáticos son muy exigentes con la forma en que se calculan sus matemáticas y pueden querer usar su propia implementación de las funciones matemáticas en lugar de la implementación estándar. Si las funciones matemáticas se agruparan libc.a, no sería posible hacerlo.

[Editar]

Sin embargo, no estoy seguro de estar de acuerdo con esto. Si tiene una biblioteca que proporciona, digamos, sqrt()y la pasa antes que la biblioteca estándar, un enlazador de Unix tomará su versión, ¿verdad?


10
No creo que haya una garantía de que eso suceda; podrías terminar con un conflicto de símbolos en su lugar. Probablemente dependerá del vinculador y el diseño de la biblioteca. Todavía encuentro que esa razón es débil; si está haciendo una función sqrt personalizada, realmente no debería darle el mismo nombre que la función sqrt estándar, incluso si hace lo mismo ...
Ephemient

1
De hecho, hacer que su propia función (no estática) dé como sqrtresultado resultados en un programa con comportamiento indefinido.
R .. GitHub DEJA DE AYUDAR AL HIELO

@Bastien Buen hallazgo. Y llegando a su punto, ¿qué quiere decir con "antes de la biblioteca estándar"? Pensé, la biblioteca estándar está vinculada de forma predeterminada y no es necesario que esté vinculada a través de las opciones de línea de comandos. Por lo tanto, la biblioteca estándar será la primera opción para el enlazador y no se puede colocar su propia implementación "antes que la biblioteca estándar".
Rocky Inde

@RockyInde: mira mi respuesta, creo que realmente quise decir "antes de la biblioteca matemática estándar". Pero creo que hay opciones de compilación para no vincular la biblioteca C estándar, lo que le permitiría pasar la suya.
Bastien Léonard

@ BastienLéonard Uso gcc de la versión 7.2, que -lmes totalmente opcional. Alguna idea
Donghua Liu

5

Hay una discusión exhaustiva sobre la vinculación a bibliotecas externas en Introducción a GCC: vinculación con bibliotecas externas . Si una biblioteca es miembro de las bibliotecas estándar (como stdio), entonces no necesita especificar al compilador (realmente el vinculador) para vincularlas.

EDITAR: Después de leer algunas de las otras respuestas y comentarios, creo que la referencia libc.a y la referencia libm a la que enlaza ambas tienen mucho que decir sobre por qué las dos están separadas.

Tenga en cuenta que muchas de las funciones en 'libm.a' (la biblioteca matemática) están definidas en 'math.h' pero no están presentes en libc.a. Algunos son confusos, pero la regla general es esta: la biblioteca C contiene aquellas funciones que ANSI dicta que deben existir, por lo que no necesita el -lm si solo usa funciones ANSI. En contraste, 'libm.a' contiene más funciones y admite funcionalidades adicionales, como la devolución de llamada de matherr y el cumplimiento de varios estándares de comportamiento alternativos en caso de errores de FP. Vea la sección libm, para más detalles.


1
Lo que no responde a la pregunta de por qué debe vincular las bibliotecas de coincidencias por separado. Obviamente, debe tener que vincular las bibliotecas OpenGL por separado, pero podría decirse que las bibliotecas matemáticas son generalmente útiles.
David Thornley

@David: Tienes razón. No estaba claro para mí por la pregunta que esto era lo que estaba preguntando el OP. Estaba editando mi respuesta como comentaste.
Bill el lagarto

Sé la razón por la que compilé un programa que usa el sqrt función y funciona sin incluir la biblioteca a través de -lm. ¡Gracias!
L_K

5

Como ephemient dijo, la biblioteca de C libc está vinculada de forma predeterminada y esta biblioteca contiene las implementaciones de stdlib.h, stdio.h y varios otros archivos de encabezado estándar. Solo para agregarle, de acuerdo con " Una introducción a GCC ", el comando de enlace para un programa básico "Hello World" en C es el siguiente:

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 la opción -lc en la tercera línea que une la biblioteca C.


3

Creo que es algo arbitrario. Debe dibujar una línea en alguna parte (qué bibliotecas son predeterminadas y cuáles deben especificarse).

Le da la oportunidad de reemplazarlo por uno diferente que tenga las mismas funciones, pero no creo que sea muy común hacerlo.

EDITAR: (de mis propios comentarios): Creo que gcc hace esto para mantener la compatibilidad con el cc original. Supongo por qué cc hace esto es por el tiempo de construcción: cc fue escrito para máquinas con mucha menos potencia que la que tenemos ahora. Muchos programas no tienen matemática de punto flotante y probablemente tomaron todas las bibliotecas que no se usaban comúnmente de manera predeterminada. Supongo que el tiempo de compilación del sistema operativo UNIX y las herramientas que lo acompañan fueron la fuerza impulsora.


Creo que la mentalidad detrás de la pregunta es que los contenidos de libm son en gran parte parte de la biblioteca estándar de C, ¿por qué no están en libc?
Evan Teran

1
El motivo por el que gcc es mantener la compatibilidad con el cc original en AT&T Unix. Usé 3B2 en 1988 y tuviste que -lm para obtener matemáticas. Me pareció completamente arbitrario en ese momento. En Visual Studio, no recuerdo haber tenido que agregar matemáticas, pero a veces hay que agregar otras bibliotecas aparentemente en tiempo de ejecución. Supongo que los vendedores del compilador tienen una razón (¿tiempo de compilación?), Pero en este momento, apuesto a que gcc solo está tratando de ser compatible con versiones anteriores.
Lou Franco

3

Si pongo stdlib.h o stdio.h, no tengo que vincularlos, pero tengo que vincular cuando compilo:

stdlib.h, stdio.h son los archivos de encabezado. Los incluye para su conveniencia. Solo pronostican qué símbolos estarán disponibles si se vincula en la biblioteca adecuada. Las implementaciones están en los archivos de la biblioteca, ahí es donde realmente viven las funciones.

Incluso math.h es solo el primer paso para obtener acceso a todas las funciones matemáticas.

Además, no tiene que vincularse libmsi no usa sus funciones, incluso si hace un#include <math.h> que es solo informativo para usted, para el compilador sobre los símbolos.

stdlib.h, stdio.hconsulte las funciones disponibles en libc, que siempre están vinculadas para que el usuario no tenga que hacerlo él mismo.


2

stdio es parte de la biblioteca estándar de C que, por defecto, gcc enlazará.

Las implementaciones de la función matemática se encuentran en un archivo libm separado que no está vinculado de forma predeterminada, por lo que debe especificarlo -lm. Por cierto, no hay relación entre esos archivos de encabezado y los archivos de la biblioteca.


3
él sabe que ... está preguntando por qué
Evan Teran

Él dice por qué. Simon explica que algunas bibliotecas están vinculadas de forma predeterminada, como stdio, mientras que la biblioteca matemática no está vinculada de forma predeterminada, por lo que debe especificarse.
mnuzzo

55
Diría que la naturaleza de la pregunta es preguntar por qué libm no está vinculado de forma predeterminada (o incluso se separa de libc) ya que su contenido es en gran parte parte de la biblioteca estándar de c.
Evan Teran

2

Yo supongo que es una manera de hacer que las aplicaciones que no utilizan en absoluto realizar un poco mejor. Aquí está mi pensamiento sobre esto.

Los sistemas operativos x86 (e imagino que otros) necesitan almacenar el estado de la FPU en el cambio de contexto. Sin embargo, la mayoría de los sistemas operativos solo se molestan en guardar / restaurar este estado después de que la aplicación intente usar la FPU por primera vez.

Además de esto, probablemente haya algún código básico en la biblioteca matemática que establecerá la FPU en un estado base sano cuando se cargue la biblioteca.

Entonces, si no vincula ningún código matemático, nada de esto sucederá, por lo tanto, el sistema operativo no tiene que guardar / restaurar ningún estado de FPU, lo que hace que los cambios de contexto sean un poco más eficientes.

Solo una suposición sin embargo.

EDITAR: en respuesta a algunos de los comentarios, la misma premisa básica todavía se aplica a los casos que no son de FPU (la premisa es que fue para hacer que las aplicaciones que no hicieron que el uso de libm funcione un poco mejor).

Por ejemplo, si hay una FPU suave que era probable en los primeros días de C. Entonces tener libm por separado podría evitar que muchos códigos grandes (y lentos si se usaran) se vinculen innecesariamente.

Además, si solo hay enlaces estáticos disponibles, se aplica un argumento similar que mantendría los tamaños ejecutables y los tiempos de compilación reducidos.


Si no se vincula con libm pero toca la FPU x87 a través de otros medios (operaciones en flotadores, por ejemplo), el núcleo x86 necesita guardar el estado de la FPU. No creo que esta es una muy buena conjetura ...
ephemient

por supuesto, si usa manualmente la FPU, el núcleo aún necesitará guardar / restaurar su estado. Estaba diciendo que si nunca lo usas (incluso si no usas libm), entonces no tendrás que hacerlo.
Evan Teran

Realmente puede depender mucho del núcleo. La biblioteca matemática que usa el kernel podría tener una función save_FPU_on_switch () que lo activa, mientras que otros solo detectan si se tocó la FPU.
Earlz

1
Si no recuerdo mal, todo el problema es anterior a los coprocesadores de coma flotante incluso en microprocesadores.
Nosredna

@earlz: el enfoque de guardar la solicitud de la biblioteca matemática sería un diseño terrible. ¿Qué pasa si usan la FPU por algún otro medio? El único enfoque sensato (además de siempre guardar / restaurar) sería detectar el uso y luego comenzar a guardar / restaurar.
Evan Teran
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.