stringtranslate.com

Función en línea

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

  1. Funciona como una directiva del compilador que sugiere (pero no requiere) que el compilador sustituya el cuerpo de la función en línea realizando una expansión en línea , es decir, insertando el código de la función en la dirección de cada llamada de función, ahorrando así la sobrecarga de una llamada de función. En este sentido, es análogo al register especificador de clase de almacenamiento , que de manera similar proporciona una sugerencia de optimización. [1]
  2. El segundo propósito de inlinees cambiar el comportamiento de los enlaces; los detalles de esto son complicados. Esto es necesario debido al modelo de compilación y enlace separados 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 en línea durante la compilación , lo que, si la función tiene un enlace externo , causa 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]

Ejemplo

Una inlinefunción se puede escribir en C o C++ de la siguiente manera:

intercambio de vacío en línea ( int * m , int * n ) { int tmp = * m ; * m = * n ; * n = tmp ; }               

Luego, una afirmación como la siguiente:

intercambiar ( & x , & y ); 

se puede traducir a (si el compilador decide realizar la incrustación, lo que generalmente 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.

Soporte estándar

C++ y C99 , pero no sus predecesores K&R C y C89 , tienen soporte para inlinefunciones, aunque con diferente semántica. En ambos casos, inlineno fuerza la inserción en línea; el compilador es libre de elegir no insertar la función en línea en absoluto, o solo en algunos casos. Los diferentes compiladores varían en la complejidad de la función que pueden manejar para insertar en línea. Los compiladores de C++ convencionales como Microsoft Visual C++ y GCC admiten una opción que permite a los compiladores insertar en línea automáticamente cualquier función adecuada, incluso aquellas que no están marcadas como inlinefunciones. Sin embargo, simplemente omitir la inlinepalabra clave para dejar que el compilador tome todas las decisiones de inserción en línea no es posible, ya que el enlazador se quejará de definiciones duplicadas en diferentes unidades de traducción. Esto se debe a que inlineno solo le da al compilador una pista de que la función debe insertarse en línea, sino que también tiene un efecto sobre si el compilador generará una copia fuera de línea invocable de la función (consulte las clases de almacenamiento de funciones en línea).

Extensiones no estándar

GNU C , como parte del dialecto gnu89 que ofrece, tiene soporte para inlineC89 como extensión. Sin embargo, la semántica difiere tanto de la de C++ como de la de C99. armcc en modo C90 también se ofrece inlinecomo una extensión no estándar, con una semántica diferente a la de gnu89 y C99.

Algunas implementaciones proporcionan un medio por el cual forzar al compilador a incorporar una función, generalmente por medio de especificadores de declaración específicos de la implementación:

El uso indiscriminado de este tipo de funciones puede generar un código más grande (archivo ejecutable inflado), una mejora mínima o nula del rendimiento y, en algunos casos, incluso una pérdida de rendimiento. Además, el compilador no puede incorporar la función en línea en todas las circunstancias, incluso cuando se fuerza la incorporación; en este caso, tanto gcc como Visual C++ generan advertencias.

Forzar la inserción en línea es útil si:

Para la portabilidad del código, se pueden utilizar las siguientes directivas de preprocesador:

#ifdef _MSC_VER #define forceinline __forceinline #elif definido(__GNUC__) #define forceinline en línea __atributo__((__siempre_en_línea__)) #elif definido(__CLANG__) #if __tiene_atributo(__siempre_en_línea__) #define forceinline en línea __atributo__((__siempre_en_línea__)) #else #define forceinline en línea #endif #else #define forceinline en línea #endif        

Clases de almacenamiento de funciones en línea

static inlinetiene los mismos efectos en todos los dialectos C y C++. Emitirá una copia visible localmente (fuera de línea) de la función si es necesario.

Independientemente de la clase de almacenamiento, el compilador puede ignorar el inlinecalificador y generar una llamada de función en todos los dialectos C y C++.

El efecto de la clase de almacenamiento externcuando se aplica o no a inlinefunciones difiere entre los dialectos C [2] y C++. [3]

C99

En C99, una función definida inlinenunca emitirá, y extern inlinesiempre emitirá, una función visible externamente. A diferencia de C++, no hay forma de solicitar que una función visible externamente compartida entre unidades de traducción se emita solo si es necesario.

Si inlinelas declaraciones se mezclan con extern inlinedeclaraciones o con declaraciones no calificadas (es decir, sin inlinecalificador o clase de almacenamiento), la unidad de traducción debe contener una definición (sin importar si es no calificada, inlineo extern inline) y se emitirá una función visible externamente para ella.

Una función definida inlinerequiere exactamente una función con ese nombre en algún otro lugar del programa que esté definida extern inlineo no tenga calificador. Si se proporciona más de una definición de este tipo en todo el programa, el enlazador se quejará de los símbolos duplicados. Sin embargo, si falta alguna, el enlazador no se queja necesariamente, porque, si todos los usos pudieran incluirse en línea, no sería necesario. Pero puede quejarse, ya que el compilador siempre puede ignorar el inlinecalificador y generar llamadas a la función en su lugar, 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 incluirse en línea en todas partes por todos los medios, y se debería generar un error si no lo está). Una forma conveniente es definir las inlinefunciones en archivos de encabezado y crear un archivo .c por función, que contenga una extern inlinedeclaración para ella e incluya el archivo de encabezado respectivo con la definición. No importa si la declaración está antes o después de la inclusión.

Para evitar que se agregue código inalcanzable al ejecutable final si todos los usos de una función se incluyen en línea, se recomienda [3] colocar los archivos de objeto de todos esos archivos .c con una sola extern inlinefunción en un archivo de biblioteca estáticaar rcs , generalmente con , y luego vincular con esa biblioteca en lugar de con los archivos de objeto individuales. Eso hace que solo se vinculen los archivos de objeto que realmente se necesitan, en contraste con vincular los archivos de 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 de objeto en la línea de comandos del enlazador, ya que el enlazador no considerará las llamadas desde los archivos de objeto especificados después del archivo de biblioteca a las funciones. El enlazador resolverá automáticamente las llamadas desde inlinefunciones a otras funciones (la opción en garantiza esto).inlinesar rcs

Una solución alternativa es utilizar la optimización del tiempo de enlace en lugar de una biblioteca. gcc proporciona la opción -Wl,--gc-sectionsde omitir las secciones en las que no se utilizan todas las funciones. Este será el caso de los archivos de objetos que contengan el código de una única extern inlinefunción no utilizada. Sin embargo, también elimina todas las demás secciones no utilizadas de todos los demás archivos de objetos, no sólo las relacionadas con extern inlinefunciones no utilizadas. (Puede que se desee vincular funciones en el ejecutable que el programador debe llamar desde el depurador en lugar de hacerlo el propio programa, por ejemplo, para examinar el estado interno del programa). Con este enfoque, también es posible utilizar un único archivo .c con todas extern inlinelas funciones en lugar de un archivo .c por función. A continuación, el archivo debe compilarse con -fdata-sections -ffunction-sections. Sin embargo, la página del manual de gcc advierte sobre ello, diciendo "Utilice estas opciones sólo cuando haya beneficios significativos al hacerlo".

Algunos recomiendan un enfoque completamente diferente, que consiste en definir funciones como static inlineen lugar de inlineen archivos de encabezado. [2] De este modo, 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 pudo incluir en línea 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 una función de este tipo definida como static inlineen un archivo de encabezado producirá valores diferentes en diferentes unidades de traducción. Por lo tanto, static inlinelas funciones solo se deben utilizar si se utilizan en una sola unidad de traducción, lo que significa que solo deben ir al archivo .c respectivo, no a un archivo de encabezado.

gnu89

La semántica de gnu89 de inliney extern inlinees esencialmente exactamente lo opuesto a la de C99, [4] con la excepción de que gnu89 permite la redefinición de una extern inlinefunción como una función no calificada, mientras que C99 inlineno. [5] Por lo tanto, gnu89 extern inlinesin redefinición es como C99 inline, y gnu89 inlinees como C99 extern inline; en otras palabras, en gnu89, una función definida inlinesiempre emitirá y una función definida extern inlinenunca emitirá una función visible externamente. La razón de esto es que coincide con las variables, para las cuales nunca se reservará almacenamiento si se define como externy siempre si se define sin. La razón de C99, en cambio, es que sería sorprendente si el uso inlinetuviera un efecto secundario (emitir siempre una versión no incorporada de la función) que es contrario a lo que sugiere su nombre.

Las observaciones 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 inlinesemántica gnu89 incluso cuando -std=c99se especificaba explícitamente. [6] Con la versión 5, [5] gcc cambió de gnu89 al dialecto gnu11, habilitando efectivamente inlinela semántica C99 por defecto. Para usar la semántica gnu89 en cambio, deben habilitarse explícitamente, ya sea con -std=gnu89o , para afectar solo a la inserción en línea , -fgnu89-inlineo agregando el gnu_inlineatributo a todas inlinelas declaraciones. Para asegurar la semántica C99, se puede usar , o (sin ) -std=c99. [ 3 ]-std=c11-std=gnu99-std=gnu11-fgnu89-inline

C++

En C++, una función definida inlineemitirá, si es necesario, una función compartida entre las unidades de traducción, normalmente colocándola en la sección común del archivo de objeto para el que se necesita. La función debe tener la misma definición en todas partes, siempre con el inlinecalificador. En C++, extern inlinees lo mismo que inline. La razón fundamental del enfoque de 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, al igual que para las funciones ordinarias, no hay diferencia si externse especifica o no.

El inlinecalificador se agrega automáticamente a una función definida como parte de una definición de clase.

brazocc

armcc en modo C90 proporciona extern inlineuna inlinesemá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 modo C99, extern inlinesiempre emite una función, pero al igual que en C++, se compartirá entre las unidades de traducción. Por lo tanto, la misma función se puede definir extern inlineen diferentes unidades de traducción. [7] Esto coincide con el comportamiento tradicional de los compiladores C de Unix [8] para múltiples externdefiniciones no inicializadas de variables globales.

Restricciones

Para tomar la dirección de una inlinefunción es necesario que se emita código para que se emita una copia no en línea de esa función en cualquier caso.

En C99, una función inlineor no debe acceder a variables globales ni definir variables no locales. Las variables locales pueden ser o no objetos diferentes en diferentes unidades de traducción, dependiendo de si la función se incluyó en línea o si se realizó una llamada. Solo las definiciones pueden hacer referencia a identificadores con enlace interno sin restricciones; estos serán objetos diferentes en cada unidad de traducción. En C++, se permiten tanto las variables locales como las no locales y hacen referencia al mismo objeto en todas las unidades de traducción.extern inlinestaticconst staticconst staticstatic inlineconstconst static

gcc no puede incorporar funciones en línea si [3]

  1. Son variádicos ,
  2. usaralloca
  3. utilizar computadorizadogoto
  4. utilizar no localgoto
  5. utilizar funciones anidadas
  6. usarsetjmp
  7. usar__builtin_longjmp
  8. utilizar __builtin_return, o
  9. usar__builtin_apply_args

Según las especificaciones de Microsoft en MSDN, MS Visual C++ no puede incorporarse en línea (ni siquiera con __forceinline), si

  1. La función o su llamador se compila con /Ob0 (la opción predeterminada para compilaciones de depuración).
  2. La función y el llamador utilizan diferentes tipos de manejo de excepciones (manejo de excepciones de C++ en uno, manejo de excepciones estructurado en el otro).
  3. La función tiene una lista de argumentos variable .
  4. La función utiliza ensamblaje en línea , a menos que se compile con /Og, /Ox, /O1 o /O2.
  5. La función es recursiva y no está acompañada por #pragma inline_recursion(on). Con pragma, las funciones recursivas se incorporan en línea a una profundidad predeterminada de 16 llamadas. Para reducir la profundidad de incorporación en línea, utilice inline_depthpragma.
  6. La función es virtual y se llama virtualmente. Las llamadas directas a funciones virtuales se pueden incluir en línea.
  7. El programa toma la dirección de la función y la llamada se realiza a través del puntero a la función. Las llamadas directas a funciones que ya tienen su dirección tomada se pueden incluir en línea.
  8. La función también está marcada con el __declspecmodificador naked.

Problemas

Además de los problemas con la expansión en línea en general (ver Expansión en línea § Efecto en el rendimiento ), inlinelas funciones como una característica del lenguaje pueden no ser tan valiosas como parecen, por varias razones:

Citas

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 debe preferir la sustitución en línea del cuerpo de la función en el punto de llamada al mecanismo de llamada de función habitual. No se requiere que una implementación realice esta sustitución en línea en el punto de llamada; sin embargo, incluso si se omite esta sustitución en línea, se respetarán 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  ... Hacer que una función sea una función en línea sugiere que las llamadas a la función sean lo más rápidas posible. La medida en que tales sugerencias son efectivas está definida por la implementación ( nota al pie: por ejemplo, una implementación podría no realizar nunca una sustitución en línea, o podría realizar únicamente sustituciones en línea a llamadas dentro del 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 usa la definición en línea o la definición externa.

—  ISO 9899:1999(E), la norma C99, sección 6.7.4

Véase también

Referencias

  1. ^ ab Meyers, Randy (1 de julio de 2002). "El nuevo C: funciones en línea". {{cite journal}}: Requiere citar revista |journal=( ayuda )
  2. ^ ab "Funciones en línea en C".
  3. ^ abcd "Usando la colección de compiladores GNU (GCC): en línea".
  4. ^ "Josef "Jeff" Sipek » GNU en línea vs. C99 en línea".
  5. ^ ab "Portación a GCC 5 - Proyecto GNU".
  6. ^ "Ian Lance Taylor - Limpieza externa en línea".
  7. ^ "Documentación – Desarrollador Arm".
  8. ^ página del manual de gcc, descripción de-fno-common

Enlaces externos