En los lenguajes de programación C y C++ , una función en línea es aquella calificada con la palabra clave ; esto tiene dos propósitos: inline
register
especificador de clase de almacenamiento , que también proporciona una sugerencia de optimización. [1]inline
es cambiar el comportamiento de vinculación; Los detalles de esto son complicados. Esto es necesario debido al modelo de compilación + enlace separado de C/C++, específicamente porque la definición (cuerpo) de la función debe duplicarse en todas las unidades de traducción donde se usa, para permitir la inserción durante la compilación , lo cual, si la función tiene características externas. linkage , provoca una colisión durante el enlace (viola la unicidad de los símbolos externos). C y C++ (y dialectos como GNU C y Visual C++) resuelven esto de diferentes maneras. [1]Una inline
función se puede escribir en C o C++ así:
intercambio vacío en línea ( int * m , int * n ) { int tmp = * m ; * m = * n ; * n = tmp ; }
Luego, una declaración como la siguiente:
intercambiar ( & x , & y );
se puede traducir a (si el compilador decide hacer la inserción, que normalmente requiere que se habilite la optimización):
int tmp = x ; x = y ; y = tmp ;
Al implementar un algoritmo de clasificación que realiza muchos intercambios, esto puede aumentar la velocidad de ejecución.
C++ y C99 , pero no sus predecesores K&R C y C89 , tienen soporte para inline
funciones, aunque con semántica diferente. En ambos casos, inline
no fuerza la inserción; el compilador es libre de elegir no incluir la función en absoluto, o sólo en algunos casos. Los diferentes compiladores varían en cuanto a la complejidad de la función que pueden implementar en línea. Los compiladores convencionales de C++ como Microsoft Visual C++ y GCC admiten una opción que permite a los compiladores alinear automáticamente cualquier función adecuada, incluso aquellas que no están marcadas como inline
funciones. Sin embargo, no es posible simplemente omitir la inline
palabra clave para permitir que el compilador tome todas las decisiones de inserción, ya que el vinculador se quejará de definiciones duplicadas en diferentes unidades de traducción. Esto se debe a que inline
no solo le da al compilador una pista de que la función debe estar integrada, sino que también tiene un efecto sobre si el compilador generará una copia fuera de línea invocable de la función (consulte clases de almacenamiento de funciones integradas).
GNU C , como parte del dialecto gnu89 que ofrece, tiene soporte inline
como extensión de C89. Sin embargo, la semántica difiere tanto de la de C++ como de la de C99. armcc en modo C90 también se ofrece inline
como una extensión no estándar, con una semántica diferente a la de gnu89 y C99.
Algunas implementaciones proporcionan un medio para forzar al compilador a alinear una función, generalmente mediante especificadores de declaración específicos de la implementación:
__forceinline
__attribute__((always_inline))
o __attribute__((__always_inline__))
, el último de los cuales es útil para evitar un conflicto con una macro definida por el usuario llamada always_inline
.Los usos indiscriminados de esto pueden resultar en un código más grande (archivo ejecutable inflado), una ganancia de rendimiento mínima o nula y, en algunos casos, incluso una pérdida de rendimiento. Además, el compilador no puede insertar la función en todas las circunstancias, incluso cuando la inserción es forzada; en este caso, tanto gcc como Visual C++ generan advertencias.
Forzar la inserción es útil si:
inline
no es respetado por el compilador (ignorado por el analizador de costo/beneficio del compilador)Para la portabilidad del código, se pueden utilizar las siguientes directivas de preprocesador:
#ifdef _MSC_VER #definir forceinline __forceinline #elif definido(__GNUC__) #definir forceinline en línea __attribute__((__always_inline__)) #elif definido(__CLANG__) #if __has_attribute(__always_inline__) #definir forceinline en línea __attribute__((__always_inline__)) #else #definir forceinline en línea #endif #else #definir fuerza en línea en línea #endif
static inline
tiene los mismos efectos en todos los dialectos de C y C++. Emitirá una función localmente visible (copia fuera de línea) si es necesario.
Independientemente de la clase de almacenamiento, el compilador puede ignorar el inline
calificador y generar una llamada de función en todos los dialectos de C y C++.
El efecto de la clase de almacenamiento extern
cuando se aplica o no a inline
funciones difiere entre los dialectos C [2] y C++. [3]
En C99, una función definida inline
nunca emitirá, y una función definida extern inline
siempre emitirá, una función visible externamente. A diferencia de C++, no hay forma de solicitar que se emita una función visible externamente compartida entre unidades de traducción solo si es necesario.
Si inline
las declaraciones se mezclan con extern inline
declaraciones o con declaraciones no calificadas (es decir, sin inline
calificador o clase de almacenamiento), la unidad de traducción debe contener una definición (sin importar si es no calificada, inline
o extern inline
) y se emitirá una función visible externamente para ella.
Una función definida inline
requiere exactamente una función con ese nombre en algún otro lugar del programa que esté definida extern inline
o sin calificador. Si se proporciona más de una definición de este tipo en todo el programa, el vinculador se quejará de símbolos duplicados. Sin embargo, si falta, el enlazador no necesariamente se queja, porque, si todos los usos pudieran incluirse, no es necesario. Pero puede quejarse, ya que el compilador siempre puede ignorar el inline
calificador y generar llamadas a la función, como suele ocurrir si el código se compila sin optimización. (Este puede ser el comportamiento deseado, si se supone que la función debe estar integrada en todas partes, y se debería generar un error si no lo está). Una forma conveniente es definir las inline
funciones en archivos de encabezado y crear un archivo .c. por función, que contiene una extern inline
declaración para la misma e incluye el archivo de encabezado respectivo con la definición. No importa si la declaración es anterior o posterior a la inclusión.
Para evitar que se agregue código inalcanzable al ejecutable final si todos los usos de una función estuvieran integrados, se recomienda [3] colocar los archivos objeto de todos los archivos .c con una sola extern inline
función en un archivo de biblioteca estática , generalmente con ar rcs
, luego vincúlelo a esa biblioteca en lugar de a los archivos de objetos individuales. Esto hace que solo se vinculen aquellos archivos objeto que realmente son necesarios, en contraste con vincular los archivos objeto directamente, lo que hace que siempre se incluyan en el ejecutable. Sin embargo, el archivo de biblioteca debe especificarse después de todos los demás archivos objeto en la línea de comando del vinculador, ya que el vinculador no considerará las llamadas desde archivos objeto especificados después del archivo de biblioteca a las funciones. El vinculador resolverá automáticamente las llamadas de inline
funciones a otras funciones (la opción garantiza esto).inline
s
ar rcs
Una solución alternativa es utilizar la optimización del tiempo de enlace en lugar de una biblioteca. gcc proporciona la bandera -Wl,--gc-sections
para omitir secciones en las que todas las funciones no se utilizan. Este será el caso de los archivos objeto que contengan el código de una única extern inline
función no utilizada. Sin embargo, también elimina todas las demás secciones no utilizadas de todos los demás archivos objeto, no solo aquellas relacionadas con extern inline
funciones no utilizadas. (Es posible que desee vincular funciones en el ejecutable que el programador debe llamar desde el depurador en lugar de hacerlo el programa mismo, por ejemplo, para examinar el estado interno del programa). Con este enfoque, también es posible utilizar un único archivo .c con todas extern inline
las funciones en lugar de un archivo .c por función. Luego el archivo debe compilarse con -fdata-sections -ffunction-sections
. Sin embargo, la página del manual de gcc advierte sobre esto y dice: "Utilice estas opciones sólo cuando obtenga beneficios significativos al hacerlo".
Algunos recomiendan un enfoque completamente diferente, que consiste en definir funciones en static inline
lugar de inline
en archivos de encabezado. [2] Entonces, no se generará ningún código inalcanzable. Sin embargo, este enfoque tiene un inconveniente en el caso opuesto: se generará código duplicado si la función no se puede insertar en más de una unidad de traducción. El código de función emitido no se puede compartir entre unidades de traducción porque debe tener direcciones diferentes. Este es otro inconveniente; tomar la dirección de dicha función definida como static inline
en un archivo de encabezado producirá diferentes valores en diferentes unidades de traducción. Por lo tanto, static inline
las funciones solo deben usarse si se usan en una sola unidad de traducción, lo que significa que solo deben ir al archivo .c respectivo, no a un archivo de encabezado.
La semántica de gnu89 inline
y extern inline
es esencialmente exactamente lo opuesto a la de C99, [4] con la excepción de que gnu89 permite la redefinición de una extern inline
función como una función no calificada, mientras que C99 inline
no. [5] Así, gnu89 extern inline
sin redefinición es como C99 inline
, y gnu89 inline
es como C99 extern inline
; en otras palabras, en gnu89, una función definida inline
siempre emitirá y una función definida extern inline
nunca emitirá una función visible externamente. La razón de esto es que coincide con variables, para las cuales el almacenamiento nunca se reservará si se define como extern
y siempre si se define sin. El fundamento de C99, por el contrario, es que sería sorprendente si el uso inline
tuviera un efecto secundario (emitir siempre una versión no integrada de la función) que sea contrario a lo que sugiere su nombre.
Los comentarios para C99 sobre la necesidad de proporcionar exactamente una instancia de función visible externamente para funciones en línea y sobre el problema resultante con código inalcanzable se aplican mutatis mutandis también a gnu89.
gcc hasta la versión 4.2 inclusive usaba inline
la semántica gnu89 incluso cuando -std=c99
se especificaba explícitamente. [6] Con la versión 5, [5] gcc cambió del dialecto gnu89 al gnu11, habilitando efectivamente inline
la semántica C99 de forma predeterminada. Para usar la semántica de gnu89 en su lugar, deben habilitarse explícitamente, ya sea con -std=gnu89
o, para afectar solo la inserción en línea, -fgnu89-inline
o agregando el gnu_inline
atributo a todas inline
las declaraciones. Para garantizar la semántica de C99, se puede utilizar -std=c99
, o (sin ). [3]-std=c11
-std=gnu99
-std=gnu11
-fgnu89-inline
En C++, una función definida inline
emitirá, si es necesario, una función compartida entre unidades de traducción, normalmente colocándola en la sección común del archivo objeto para el que se necesita. La función debe tener la misma definición en todas partes, siempre con el inline
calificador. En C++, extern inline
es lo mismo que inline
. El fundamento del enfoque C++ es que es la forma más conveniente para el programador, ya que no se deben tomar precauciones especiales para la eliminación de código inalcanzable y, como ocurre con las funciones ordinarias, no hay diferencia si extern
se especifica o no.
El inline
calificador se agrega automáticamente a una función definida como parte de una definición de clase.
armcc en modo C90 proporciona extern inline
una inline
semántica que es la misma que en C++: dichas definiciones emitirán una función compartida entre las unidades de traducción si es necesario. En el modo C99, extern inline
siempre emite una función, pero al igual que en C++, se compartirá entre las unidades de traducción. Por tanto, la misma función se puede definir extern inline
en diferentes unidades de traducción. [7] Esto coincide con el comportamiento tradicional de los compiladores de Unix C [8] para múltiples no extern
definiciones de variables globales no inicializadas.
Tomar la dirección de una inline
función requiere código para que se emita una copia no incorporada de esa función en cualquier caso.
En C99, una función inline
o extern inline
no debe acceder static
a variables globales ni definir const
static
variables no locales. const static
Las variables locales pueden o no ser objetos diferentes en diferentes unidades de traducción, dependiendo de si la función estaba incorporada o si se realizó una llamada. Sólo static inline
las definiciones pueden hacer referencia a identificadores con vínculo interno sin restricciones; esos serán objetos diferentes en cada unidad de traducción. En C++, se permiten tanto los const
no locales como los no const
static
locales y hacen referencia al mismo objeto en todas las unidades de traducción.
gcc no puede insertar funciones en línea si [3]
alloca
goto
goto
setjmp
__builtin_longjmp
__builtin_return
, o__builtin_apply_args
Según las especificaciones de Microsoft en MSDN, MS Visual C++ no puede estar en línea (ni siquiera con __forceinline
), si
#pragma inline_recursion(on)
. Con pragma, las funciones recursivas están integradas a una profundidad predeterminada de 16 llamadas. Para reducir la profundidad del revestimiento, utilice inline_depth
pragma.__declspec
modificador desnudo.Además de los problemas con la expansión en línea en general (consulte Expansión en línea § Efecto sobre el rendimiento ), inline
las funciones como característica del lenguaje pueden no ser tan valiosas como parecen, por varias razones:
inline
función) queda expuesto a su cliente (la función que llama).inline
en C99 requiere exactamente una definición externa de la función, si se usa en alguna parte. Si el programador no proporcionó dicha definición, eso puede conducir fácilmente a errores del vinculador. Esto puede suceder con la optimización desactivada, lo que normalmente impide la inserción. Agregar definiciones, por otro lado, puede causar código inalcanzable si el programador no lo evita con cuidado, colocándolos en una biblioteca para vincularlos, usando la optimización del tiempo de vínculo o static inline
.inline
función en cada módulo (unidad de traducción) que la utiliza, mientras que una función ordinaria debe definirse en un solo módulo. De lo contrario, no sería posible compilar un único módulo independientemente de todos los demás módulos. Dependiendo del compilador, esto puede hacer que cada archivo objeto respectivo contenga una copia del código de la función, para cada módulo con algún uso que no se pueda incluir.Una declaración de función ... con un especificador en línea declara una función en línea. El especificador en línea indica a la implementación que se prefiere la sustitución en línea del cuerpo de la función en el punto de llamada al mecanismo habitual de llamada a función. No se requiere una implementación para realizar esta sustitución en línea en el punto de llamada; sin embargo, incluso si se omite esta sustitución en línea, se seguirán respetando las demás reglas para funciones en línea definidas en 7.1.2.
— ISO/IEC 14882:2011, el estándar C++ actual, sección 7.1.2
Una función declarada con un especificador de función en línea es una función en línea ... Convertir una función en una función en línea sugiere que las llamadas a la función sean lo más rápidas posible. El grado en que dichas sugerencias son efectivas está definido por la implementación ( nota al pie: por ejemplo, es posible que una implementación nunca realice una sustitución en línea o que solo realice sustituciones en línea de llamadas en el alcance de una declaració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.
— ISO 9899:1999(E), norma C99, sección 6.7.4
{{cite journal}}
: Citar diario requiere |journal=
( ayuda )-fno-common