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. Sirve 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 a la función, ahorrando así la sobrecarga de una función. llamar. En este sentido es análogo al register especificador de clase de almacenamiento , que también proporciona una sugerencia de optimización. [1]
  2. El segundo propósito inlinees 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]

Ejemplo

Una inlinefunció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.

Soporte estándar

C++ y C99 , pero no sus predecesores K&R C y C89 , tienen soporte para inlinefunciones, aunque con semántica diferente. En ambos casos, inlineno 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 inlinefunciones. Sin embargo, no es posible simplemente omitir la inlinepalabra 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 inlineno 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).

Extensiones no estándar

GNU C , como parte del dialecto gnu89 que ofrece, tiene soporte inlinecomo 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 inlinecomo 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:

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:

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        

Clases de almacenamiento de funciones en línea

static inlinetiene 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 inlinecalificador y generar una llamada de función en todos los dialectos de 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 una función definida extern inlinesiempre 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 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 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 inlinecalificador 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 inlinefunciones en archivos de encabezado y crear un archivo .c. por función, que contiene una extern inlinedeclaració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 inlinefunció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 inlinefunciones a otras funciones (la opción 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 bandera -Wl,--gc-sectionspara 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 inlinefunció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 inlinefunciones 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 inlinelas 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 inlinelugar de inlineen 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 inlineen un archivo de encabezado producirá diferentes valores en diferentes unidades de traducción. Por lo tanto, static inlinelas 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.

gnu89

La semántica de gnu89 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] Así, 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 variables, para las cuales el almacenamiento nunca se reservará si se define como externy siempre si se define sin. El fundamento de C99, por el contrario, es que sería sorprendente si el uso inlinetuviera 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 inlinela semántica gnu89 incluso cuando -std=c99se especificaba explícitamente. [6] Con la versión 5, [5] gcc cambió del dialecto gnu89 al gnu11, habilitando efectivamente inlinela semántica C99 de forma predeterminada. Para usar la semántica de gnu89 en su lugar, deben habilitarse explícitamente, ya sea con -std=gnu89o, para afectar solo la inserción en línea, -fgnu89-inlineo agregando el gnu_inlineatributo a todas inlinelas declaraciones. Para garantizar la semántica de C99, se puede utilizar -std=c99, o (sin ). [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 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 inlinecalificador. En C++, extern inlinees 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 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 el modo C99, extern inlinesiempre 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 inlineen diferentes unidades de traducción. [7] Esto coincide con el comportamiento tradicional de los compiladores de Unix C [8] para múltiples no externdefiniciones de variables globales no inicializadas.

Restricciones

Tomar la dirección de una inlinefunción requiere código para que se emita una copia no incorporada de esa función en cualquier caso.

En C99, una función inlineo extern inlineno debe acceder statica variables globales ni definir const staticvariables no locales. const staticLas 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 inlinelas 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 constno locales como los no const staticlocales y hacen referencia al mismo objeto en todas las unidades de traducción.

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

  1. son variados ,
  2. usaralloca
  3. utilizar calculadogoto
  4. usar no localgoto
  5. usar 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 estar 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 la persona que llama utilizan diferentes tipos de manejo de excepciones (manejo de excepciones C++ en uno, manejo de excepciones estructurado en el otro).
  3. La función tiene una lista de argumentos variables .
  4. La función utiliza un ensamblado en línea , a menos que esté compilada con /Og, /Ox, /O1 o /O2.
  5. La función es recursiva y no va acompañada de #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_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 mediante el puntero a la función. Las llamadas directas a funciones a las que se les ha tomado la dirección se pueden incluir en línea.
  8. La función también está marcada con el __declspecmodificador desnudo.

Problemas

Además de los problemas con la expansión en línea en general (consulte Expansión en línea § Efecto sobre el rendimiento ), inlinelas funciones como 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 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

Ver también

Referencias

  1. ^ ab Meyers, Randy (1 de julio de 2002). "La nueva C: funciones en línea". {{cite journal}}: Citar diario requiere |journal=( ayuda )
  2. ^ ab "Funciones en línea en C".
  3. ^ abcd "Uso de la colección de compiladores GNU (GCC): en línea".
  4. ^ "Josef" Jeff "Sipek» GNU en línea frente a C99 en línea ".
  5. ^ ab "Migración a GCC 5 - Proyecto GNU".
  6. ^ "Ian Lance Taylor - Limpiar externo en línea".
  7. ^ "Documentación: desarrollador de Arm".
  8. ^ página del manual de gcc, descripción de-fno-common

enlaces externos