2009-06-23 5 views
201

Si incluyo <stdlib.h> o <stdio.h> en un programa C que no tiene que vincular éstos al compilar pero sí tienen que enlazar a <math.h>, utilizando -lm con gcc, por ejemplo:¿Por qué tiene que vincular la biblioteca de matemáticas en C?

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?

Respuesta

188

Las funciones en stdlib.h y stdio.h tienen implementaciones en libc.so (o libc.a para la vinculación estática), que está vinculado en su ejecutable por defecto (como si -lc fueron especificados). Se puede indicar a GCC que evite este enlace automático con las opciones -nostdlib o -nodefaultlibs.

las funciones matemáticas en math.h tienen implementaciones en libm.so (o libm.a para la vinculación estática), y libm no está ligada en forma predeterminada. Existen razones históricas para esta división libm/libc, ninguna de ellas muy convincente.

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

+0

OK, entonces esto se convierte en una pregunta de diseño de la biblioteca: ¿por qué las bibliotecas están particionadas de esta manera? –

+1

Apuesto a optimizar el tiempo de compilación de UNIX (y el conjunto de herramientas que lo acompaña). En ese momento, ¿era probablemente la cosa más compleja que se estaba construyendo en C. –

+0

tener algo que ver con la emulación fpu? pero para Linux, la emulación final está en kernel y no en la libm ... ¿verdad? –

3

stdio es parte de la biblioteca C estándar que, de forma predeterminada, gcc vinculará.

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

+2

él sabe que ... él pregunta * por qué * –

+1

¿Eso no explica "por qué"? –

+0

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

3

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

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

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

+0

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

+1

El motivo de 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 se deben agregar otras bibliotecas aparentemente en tiempo de ejecución. Supongo que los proveedores de compiladores 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. –

2

Supongo Supongo que es una forma de hacer que las aplicaciones que no la usan en absoluto rinden un poco mejor. Aquí está mi pensamiento sobre esto.

x86 Los sistemas operativos (y me imagino otros) necesitan almacenar el estado de FPU al cambiar 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, es probable que exista 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.

Por lo tanto, si no enlaza 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, haciendo que los cambios de contexto sean un poco más eficientes.

Sólo una suposición.

EDIT: En respuesta a algunos de los comentarios, la misma premisa básica sigue siendo válida para casos que no sean FPU (la premisa es que las aplicaciones que no hacen funcionar libm funcionan un poco mejor).

Por ejemplo, si hay una FPU suave que era muy probable en los primeros días de C. Luego, separar una libma podría evitar que un código grande (y lento si se usara) se vincule innecesariamente en.

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

+0

Si no enlaza con libm pero toca la FPU x87 por otros medios (operaciones en flotadores, por ejemplo), el kernel x86 necesita guardar el estado FPU. No creo que sea una buena suposición ... – ephemient

+0

, por supuesto, si usas manualmente la FPU, el kernel aún tendrá que guardar/restaurar su estado. Estaba diciendo que si nunca lo usas (incluido el no usar libm) entonces no tendrá que hacerlo. –

+0

Realmente puede depender mucho del kernel. La biblioteca matemática que utiliza el kernel podría tener una función save_FPU_on_switch() que la enciende, mientras que otras solo detectan si se tocó la FPU. – Earlz

4

Hay una discusión a fondo sobre el enlace a bibliotecas externas en An Introduction to GCC - Linking with external libraries. 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 el libc.a reference y la referencia de libm que vincula a ambos tienen mucho que decir acerca de por qué los dos están separados.

Tenga en cuenta que muchas de las funciones en 'libm.a' (la biblioteca matemática) se definen en 'math.h' pero no están presentes en libc.a. Algunos lo son, lo que puede confundir, pero la regla general es la siguiente: la biblioteca C contiene las funciones que ANSI dicta debe existir, por lo que no necesita el -lm si solo usa funciones ANSI. Por el contrario, `libm.a 'contiene más funciones y admite funciones adicionales, como la devolución de llamada matherr y el cumplimiento de varios estándares de comportamiento alternativos en caso de errores de FP. Ver la sección libm, para más detalles.

+1

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

+0

@David: lo tienes claro. No estaba claro para mí de la pregunta que esto era lo que el OP estaba preguntando. Estaba editando mi respuesta como usted comentó. –

+0

Sé por qué compilé un programa que usa la función 'sqrt' y funciona sin incluir la biblioteca mediante' -lm'. ¡Gracias! – huachengzan

3

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

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

Incluir math.h es solo el primer paso para acceder a todas las funciones matemáticas.

Además, no tiene que enlazar con libm si no utiliza sus funciones, incluso si hace un #include <math.h>, que es solo un paso informativo para usted, para el compilador sobre los símbolos.

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

60

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 la aritmética de enteros de 32 bits. Muchas de estas implementaciones ni siquiera tienen ¡una biblioteca matemática de punto flotante disponible!

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

Para hacer todo ese cálculo en coma flotante, necesitabas una biblioteca bastante grande. Y las matemáticas iban a ser lentas Entonces, rara vez usabas carrozas. Intentó hacer todo con enteros o enteros escalados. Cuando tenía que incluir math.h, apretaba los dientes. A menudo, debe escribir sus propias aproximaciones y tablas de búsqueda para evitarlo.

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

Es por eso que 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 han existido y las flotantes y los dobles siempre son baratos para operar, sin duda habría habido un "stdmath".

+0

Heh, estoy usando Pade approximants para (1 + x)^y en Java, en una PC de escritorio. Log, exp y pow siguen siendo lentos. –

+0

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

+7

Esto explica por qué 'libm' no está vinculado por defecto, pero math era * estándar * de C89 y antes de eso, K & R tenía * de facto * estandarizado, por lo que su comentario" stdmath "no tiene sentido. –

5

Como dijo ephemient, la biblioteca C libc está vinculada por defecto y esta biblioteca contiene las implementaciones de stdlib.h, stdio.h y varios otros archivos de encabezado estándar. Sólo para añadir a la misma, de acuerdo con "An Introduction to GCC" comando de enlace para un programa básico "Hello World" en C es la 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 

Aviso la opción -lc en la tercera línea que une el C biblioteca.

25

Se da una explicación here:

Así que si su programa está utilizando funciones matemáticas e incluyendo math.h, entonces usted necesidad de vincular explícitamente la biblioteca matemática pasando la opción -lm. 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 es posible que quieran usar su propia implementación de las funciones matemáticas en lugar de la implementación estándar. Si las funciones matemáticas se agruparon en libc.a, no sería posible hacer eso.

[Editar]

No estoy seguro estoy de acuerdo con esto, sin embargo. Si tiene una biblioteca que proporciona, por ejemplo, sqrt(), y la pasa ante la biblioteca estándar, un enlazador de Unix tomará su versión, ¿verdad?

+8

No creo que haya una garantía de que eso suceda; puede terminar con un conflicto de símbolos en su lugar. Probablemente dependería del enlazador y el diseño de la biblioteca. Todavía encuentro esa razón para ser 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) se llame ' sqrt' da como resultado un programa con un comportamiento indefinido. –

+0

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

Cuestiones relacionadas