software enlazadores ejemplos codigo c++ c linker inline

c++ - codigo - enlazadores ejemplos



¿Qué hacen realmente los enlazadores con las funciones `en línea` definidas de manera múltiple? (4)

Tanto en C como en C ++, las funciones en inline con vinculación externa pueden tener, por supuesto, múltiples definiciones disponibles en tiempo de enlace, suponiendo que estas definiciones son todas (con suerte) idénticas. (Por supuesto, me estoy refiriendo a las funciones declaradas con la especificación de vinculación en inline , no a las funciones que el compilador o el optimizador de tiempo de enlace realmente inscriben).

Entonces, ¿qué hacen normalmente los vinculadores cuando se encuentran con múltiples definiciones de una función? En particular:

  • ¿Están todas las definiciones incluidas en el ejecutable final o en la biblioteca compartida?
  • ¿Todas las invocaciones de la función se vinculan contra la misma definición?
  • ¿Las respuestas a las preguntas anteriores son requeridas por uno o más de los estándares ISO C y C ++, y si no, las plataformas más comunes hacen lo mismo?

PD Sí, sé que C y C ++ son idiomas separados, pero ambos admiten en inline , y su salida de compilador generalmente puede estar vinculada por el mismo enlazador (por ejemplo, ld de GCC), así que creo que no puede haber ninguna diferencia entre ellos en este aspecto .


Creo que la respuesta correcta a su pregunta es "depende".

Considera seguir fragmentos de código:

Archivo xc (o x.cc):

#include <stdio.h> void otherfunction(void); inline void inlinefunction(void) { printf("inline 1/n"); } int main(void) { inlinefunction(); otherfunction(); return 0; }

Archivo yc (o y.cc)

#include <stdio.h> inline void inlinefunction(void) { printf("inline 2/n"); } void otherfunction(void) { printf("otherfunction/n"); inlinefunction(); }

Como la palabra clave en inline es solo una "sugerencia" para la compilación para alinear la función, los diferentes compiladores con banderas diferentes se comportan de manera diferente. Por ejemplo, parece que el compilador de C siempre "exporta" funciones en línea y no permite múltiples definiciones:

$ gcc x.c y.c && ./a.out /tmp/ccy5GYHp.o: In function `inlinefunction'': y.c:(.text+0x0): multiple definition of `inlinefunction'' /tmp/ccQkn7m4.o:x.c:(.text+0x0): first defined here collect2: ld returned 1 exit status

mientras que C ++ lo permite:

$ g++ x.cc y.cc && ./a.out inline 1 otherfunction inline 1

Más interesante: intentemos cambiar el orden de los archivos (y por lo tanto, cambiemos el orden de los enlaces):

$ g++ y.cc x.cc && ./a.out inline 2 otherfunction inline 2

Bueno ... ¡parece que el primero cuenta! Pero ... agreguemos algunos indicadores de optimización:

$ g++ y.cc x.cc -O1 && ./a.out inline 1 otherfunction inline 2

Y ese es el comportamiento que esperaríamos. La función tiene inline. El orden diferente de los archivos no cambia nada:

$ g++ x.cc y.cc -O1 && ./a.out inline 1 otherfunction inline 2

A continuación, podemos extender nuestra fuente xc (x.cc) con prototipo de función void anotherfunction(void) y llamarla a nuestra función main . Pongamos anotherfunction definición de función en el archivo zc (z.cc):

#include <stdio.h> void inlinefunction(void); void anotherfunction(void) { printf("anotherfunction/n"); inlinefunction(); }

No definimos el cuerpo de la inlinefunction esta vez. La compilación / ejecución para c ++ da los siguientes resultados:

$ g++ x.cc y.cc z.cc && ./a.out inline 1 otherfunction inline 1 anotherfunction inline 1

Orden diferente:

$ g++ y.cc x.cc z.cc && ./a.out inline 2 otherfunction inline 2 anotherfunction inline 2

Mejoramiento:

$ g++ x.cc y.cc z.cc -O1 && ./a.out /tmp/ccbDnQqX.o: In function `anotherfunction()'': z.cc:(.text+0xf): undefined reference to `inlinefunction()'' collect2: ld returned 1 exit status

Así que la conclusión es: lo mejor es declarar en inline junto con static , lo que reduce el alcance del uso de la función, porque "exportar" la función que nos gustaría utilizar en línea no tiene sentido.


El vinculador solo tiene que descubrir cómo deduplicar todas las definiciones. Por supuesto, siempre que se haya emitido alguna definición de función; las funciones en línea bien pueden estar en línea. Pero si toma la dirección de una función en línea con enlace externo, siempre obtendrá la misma dirección (consulte [dcl.fct.spec] / 4).

Las funciones en línea no son la única construcción que requieren soporte de enlazador; las plantillas son otra, al igual que las variables en línea (en C ++ 17).


Si la función está, de hecho, en línea, entonces no hay nada para vincular. Solo cuando, por la razón que sea, el compilador decide no expandir la función en línea que tiene que generar una versión fuera de línea de la función. Si el compilador genera una versión fuera de línea de la función para más de una unidad de traducción, terminará con más de un archivo objeto con definiciones para la misma función "en línea".

La definición fuera de línea se compila en el archivo objeto y está marcada para que el enlazador no se queje si hay más de una definición de ese nombre. Si hay más de uno, el enlazador simplemente elige uno. Por lo general, es el primero que vio, pero eso no es obligatorio, y si las definiciones son todas iguales, no importa. Y es por eso que es un comportamiento indefinido tener dos o más definiciones diferentes de la misma función en línea: no hay una regla para elegir. Cualquier cosa puede suceder.


inline o sin inline , C no permite múltiples definiciones externas del mismo nombre entre las unidades de traducción que contribuyen al mismo programa o biblioteca. Además, no permite múltiples definiciones del mismo nombre en la misma unidad de traducción, ya sea interna, externa o en línea. Por lo tanto, puede haber como máximo dos definiciones disponibles de una función dada en el alcance en cualquier unidad de traducción dada: una interna y / o en línea, y una externa.

C 2011, 6.7.4 / 7 dice lo siguiente:

Cualquier función con enlace interno puede ser una función en línea. Para una función con enlace externo, se aplican las siguientes restricciones: si una función se declara con un especificador de función en inline , también se definirá en la misma unidad de traducción. Si todas las declaraciones de alcance de archivo para una función en una unidad de traducción incluyen el especificador de función en inline sin extern , entonces la definición en esa unidad de traducción es una definición en línea. Una definición en línea no proporciona una definición externa para la función, y no prohíbe una definición externa en otra unidad de traducción. Una definición en línea proporciona una alternativa a una definición externa, que un traductor puede usar para implementar cualquier llamada a la función en la misma unidad de traducción. No se especifica si una llamada a la función utiliza la definición en línea o la definición externa.

(Énfasis añadido.)

En respuesta específica a sus preguntas, entonces, en lo que respecta a C:

¿Están todas las definiciones incluidas en el ejecutable final o en la biblioteca compartida?

Las definiciones en línea no son definiciones externas. Pueden o no incluirse como funciones reales, como código en línea, ambos, o ninguno, dependiendo de las debilidades del compilador y el enlazador y de los detalles de su uso. En ningún caso son invocables por nombre por funciones de diferentes unidades de traducción, por lo tanto, si deben considerarse "incluidas" es una pregunta abstracta.

¿Todas las invocaciones de la función se vinculan contra la misma definición?

C no especifica, pero permite que la respuesta sea "no", incluso para llamadas diferentes dentro de la misma unidad de traducción. Además, las funciones en línea no son externas, por lo que ninguna función en línea definida en una unidad de traducción se llama (directamente) por una función definida en una unidad de traducción diferente.

¿Las respuestas a las preguntas anteriores son requeridas por uno o más de los estándares ISO C y C ++, y si no, las plataformas más comunes hacen lo mismo?

Mis respuestas se basan en el estándar actual de C en la medida en que aborda las preguntas, pero como habrá visto, esas respuestas no son totalmente prescriptivas. Además, el estándar no aborda directamente ninguna cuestión de código objeto o enlace, por lo que puede haber notado que mis respuestas no están, en su mayoría, expresadas en esos términos.

En cualquier caso, no es seguro suponer que cualquier sistema C dado sea coherente incluso en este aspecto para diferentes funciones o en diferentes contextos. En algunas circunstancias, puede alinear cada llamada a una función interna o en línea, de modo que esa función no aparezca en absoluto como una función separada. En otras ocasiones, de hecho puede emitir una función con enlace interno, pero eso no impide que se realicen algunas llamadas a esa función de todos modos. En cualquier caso, las funciones internas no son elegibles para vincularse con funciones de otras unidades de traducción, por lo que el vinculador no está necesariamente involucrado con vincularlas.