De Wikipedia, la enciclopedia libre
Saltar a navegación Saltar a búsqueda

En los lenguajes de programación C y C ++ , una función en línea es una calificada con la palabra clave ; esto tiene dos propósitos. En primer lugar, 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 función en la dirección de cada llamada de función, ahorrando así la sobrecarga de una llamada de función. A este respecto, es análogo al especificador de clase de almacenamiento , que de manera similar proporciona una sugerencia de optimización. [1] El segundo propósito de inlineregister inlinees cambiar el comportamiento de vinculación; los detalles de esto son complicados. Esto es necesario debido a la compilación separada de C / C ++ + modelo de vinculación, 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 , que, si la función tiene funciones externas vinculación , provoca una colisión durante la vinculación (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 [ editar ]

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:

intercambio ( & x ,  & y );

puede traducirse en (si el compilador decide hacer la inserción, que normalmente requiere que la optimización esté habilitada):

int  tmp  =  x ; x  =  y ; y  =  tmp ;

Cuando se implementa un algoritmo de clasificación haciendo muchos intercambios, esto puede aumentar la velocidad de ejecución.

Soporte estándar [ editar ]

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; el compilador es libre de elegir no alinear la función en absoluto, o solo en algunos casos. Los diferentes compiladores varían en la complejidad de la función que pueden administrar para integrar. Los compiladores convencionales de C ++ como Microsoft Visual C ++ y GCC admiten una opción que permite a los compiladores en línea automáticamente cualquier función adecuada, incluso aquellas que no están marcadas como inlinefunciones. Sin embargo, simplemente omitiendo elinlineLa palabra clave para permitir que el compilador tome todas las decisiones de inserción no es posible, 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 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 [ editar ]

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

Algunas implementaciones proporcionan un medio para obligar al compilador a insertar una función, generalmente mediante especificadores de declaración específicos de la implementación:

  • Microsoft Visual C ++: __forceinline
  • gcc o clang: __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 eso 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 alinear la función en todas las circunstancias, incluso cuando la alineación está forzada; en este caso, tanto gcc como Visual C ++ generan advertencias.

Forzar la alineación es útil si

  • inline no es respetado por el compilador (ignorado por el analizador de costo / beneficio del compilador), y
  • la alineación da como resultado un aumento de rendimiento necesario

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 inline __attribute __ ((__ always_inline__)) #elif definido (__ CLANG__)  #if __has_attribute (__ always_inline__)  #define forceinline inline __attribute __ ((__ always_inline__))  #else  #define forceinline inline  #endif #else  #define forceinline línea #endif

Clases de almacenamiento de funciones en línea [ editar ]

static inlinetiene los mismos efectos en todos los dialectos C y C ++. Emitirá una función 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 [ editar ]

En C99, una función definida inlinenunca, y una función definida 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 las unidades de traducción se emita solo si es necesario.

Si las inlinedeclaraciones 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 no calificada inline, o 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 de estas definiciones en todo el programa, el enlazador se quejará de los símbolos duplicados. Sin embargo, si falta, el enlazador no necesariamente se queja, porque, si todos los usos pudieran estar en línea, no es 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 está insertada en todas partes por todos los medios, y se debe generar un error si no lo está). Una forma conveniente es definir elinlinefunciones en archivos de encabezado y cree un archivo .c por función, que contenga una extern inlinedeclaración e incluya 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 están en línea, se recomienda [3] colocar los archivos objeto de todos esos archivos .c con una sola extern inlinefunción en un archivo de biblioteca estático , generalmente con ar rcs, luego vincular a esa biblioteca en lugar de a los archivos de objetos 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 vinculador, ya que el vinculador no considerará las llamadas de los archivos de objeto especificados después del archivo de biblioteca a las funciones. Llamadas deinlinefunciones a otras inlinefunciones serán resueltas automáticamente por el enlazador (la sopción en ar rcsasegura esto).

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 no se utilizan todas las funciones. Este será el caso de los archivos objeto que contienen 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 objeto, no solo las relacionadas con las extern inlinefunciones no utilizadas . (Se puede desear vincular funciones al ejecutable que el programador debe llamar desde el depurador en lugar de hacerlo el programa en sí, por ejemplo, para examinar el estado interno del programa). Con este enfoque, también es posible para usar un solo archivo .c con todas las extern inlinefunciones en lugar de un solo 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 eso, diciendo "Solo use estas opciones cuando haya 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 contrario: se generará un 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, las static inlinefunciones 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 [ editar ]

La semántica de gnu89 de inliney extern inlineson esencialmente opuestas a las 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 y una función definida extern inlinenunca emitirá una función visible externamente. El motivo 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 del C99, por el contrario, es que sería asombroso si se utilizarainline tendría un efecto secundario (emitir siempre una versión no alineada de la función) que es 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 el código inalcanzable se aplican mutatis mutandis a gnu89 también.

gcc hasta la versión 4.2 inclusive usaba la inlinesemántica de gnu89 incluso cuando -std=c99se especificaba explícitamente. [6] Con la versión 5, [5] gcc cambió de gnu89 al dialecto gnu11, habilitando efectivamente la inlinesemántica C99 por defecto. Para usar la semántica de gnu89 en su lugar, deben habilitarse explícitamente, ya sea con -std=gnu89o, para afectar solo al inlining -fgnu89-inline, o agregando el gnu_inlineatributo a todas las inlinedeclaraciones. Para asegurar la semántica C99, ya sea -std=c99, -std=c11, -std=gnu99o -std=gnu11(sin -fgnu89-inline) pueden ser utilizados. [3]

C ++ [ editar ]

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 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 de C ++ es que es la forma más conveniente para el programador, ya que no se deben tomar precauciones especiales para eliminar el código inalcanzable y, al igual que para las funciones ordinarias, no importa si externse especifica o no.

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

armcc [ editar ]

armcc en el modo de C90 proporciona extern inliney inlinela semántica que son los mismos que en C ++: Tales definiciones emitirán una función compartida entre las unidades de traducción si se requiere. 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 Unix C [8] para múltiples no externdefiniciones de variables globales no inicializadas.

Restricciones [ editar ]

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

En C99, una función inlineo extern inlineno debe acceder a staticvariables 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 en línea o si se realizó una llamada. Solo las static inlinedefiniciones pueden hacer referencia a identificadores con vínculos internos sin restricciones; esos serán objetos diferentes en cada unidad de traducción. En C ++, se permiten tanto los constno const staticlocales como los no locales y se refieren al mismo objeto en todas las unidades de traducción.

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

  1. son variadas ,
  2. usar alloca
  3. uso calculado goto
  4. usar no local goto
  5. usar funciones anidadas
  6. usar setjmp
  7. usar __builtin_longjmp
  8. usar __builtin_return, o
  9. usar __builtin_apply_args

Según las especificaciones de Microsoft en MSDN, MS Visual C ++ no se puede integrar (ni siquiera con __forceinline), si

  1. La función o su llamador se compila con / Ob0 (la opción predeterminada para las 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 variables .
  4. La función usa ensamblado 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 de #pragma inline_recursion(on). Con el pragma, las funciones recursivas están integradas a una profundidad predeterminada de 16 llamadas. Para reducir la profundidad de alineación, use inline_depthpragma.
  6. La función es virtual y se llama virtualmente. Se pueden incorporar llamadas directas a funciones virtuales.
  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 su dirección se pueden insertar en línea.
  8. La función también está marcada con el __declspecmodificador desnudo .

Problemas [ editar ]

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

  • A menudo, un compilador está en una mejor posición que un humano para decidir si una función en particular debe estar en línea. A veces, es posible que el compilador no pueda integrar tantas funciones como indica el programador.
  • Un punto importante a tener en cuenta es que el código (de la inlinefunción) se expone a su cliente (la función que llama).
  • A medida que las funciones evolucionan, pueden volverse adecuadas para alinear donde antes no estaban, o dejar de ser adecuadas para alinearse donde antes. Si bien incorporar o quitar una función es más fácil que convertirla desde y hacia macros, aún requiere un mantenimiento adicional que, por lo general, produce relativamente pocos beneficios.
  • Las funciones en línea utilizadas en la proliferación en los sistemas de compilación nativos basados ​​en C pueden aumentar el tiempo de compilación, ya que la representación intermedia de sus cuerpos se copia en cada sitio de llamada.
  • La especificación de inlineen 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 evita la inserción. Agregar las definiciones, por otro lado, puede causar un código inalcanzable si el programador no lo evita con cuidado, colocándolo en una biblioteca para vincularlo, usando la optimización del tiempo de enlace, o static inline.
  • En C ++, es necesario definir una inlinefunción en cada módulo (unidad de traducción) que la usa, mientras que una función ordinaria debe definirse en un solo módulo. De lo contrario, no sería posible compilar un solo módulo independientemente de todos los demás módulos. Dependiendo del compilador, esto puede hacer que cada archivo de objeto respectivo contenga una copia del código de la función, para cada módulo con algún uso que no podría estar en línea.
  • En el software embebido , a menudo es necesario colocar ciertas funciones en ciertas secciones de código mediante el uso de instrucciones especiales del compilador, como declaraciones "pragma". A veces, una función en un segmento de memoria puede necesitar llamar a una función en otro segmento de memoria, y si se produce la inserción de la función llamada, entonces el código de la función llamada puede terminar en un segmento donde no debería estar. Por ejemplo, los segmentos de memoria de alto rendimiento pueden estar muy limitados en el espacio de código, y si una función que pertenece a dicho espacio llama a otra función grande que no está destinada a estar en la sección de alto rendimiento y la función llamada se inserta de manera inapropiada, entonces esto podría hacer que el segmento de memoria de alto rendimiento se quede sin espacio de código. Por esta razón,a veces es necesario asegurarse de que las funcionesno se inline.

Citas [ editar ]

"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 la sustitución en línea del cuerpo de la función en el punto de llamada es preferible al mecanismo habitual de llamada de función. Una implementación no es necesario 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, las otras reglas para las funciones en línea definidas en 7.1.2 se seguirán respetando ".
- 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 nunca puede realizar una sustitución en línea, o puede que solo realice sustituciones en línea para 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 utilizar 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), el estándar C99, sección 6.7.4

Ver también [ editar ]

  • Macro (informática)

Referencias [ editar ]

  1. ↑ a b Meyers, Randy (1 de julio de 2002). "La nueva C: funciones en línea" . Cite journal requiere |journal=( ayuda )
  2. ^ a b http://www.greenend.org.uk/rjk/tech/inline.html
  3. ^ a b c d https://gcc.gnu.org/onlinedocs/gcc-7.1.0/gcc/Inline.html
  4. ^ http://blahg.josefsipek.net/?p=529
  5. ^ a b https://gcc.gnu.org/gcc-5/porting_to.html
  6. ^ https://gcc.gnu.org/ml/gcc-patches/2007-02/msg00119.html
  7. ^ http://infocenter.arm.com/help/topic/com.arm.doc.faqs/ka15831.html
  8. ^ página de manual de gcc, descripción de-fno-common
  • JANA, DEBASISH (1 de enero de 2005). C ++ Y PARADIGMA DE PROGRAMACIÓN ORIENTADA A OBJETOS . PHI Learning Pvt. Ltd. ISBN 978-81-203-2871-6.
  • Sengupta, Probal (1 de agosto de 2004). Programación Orientada a Objetos: Fundamentos y Aplicaciones . PHI Learning Pvt. Ltd. ISBN 978-81-203-1258-6.
  • Svenk, Goran (2003). Programación orientada a objetos: uso de C ++ para ingeniería y tecnología . Aprendizaje Cengage. ISBN 0-7668-3894-3.
  • Balagurusamy (2013). Programación orientada a objetos con C ++ . Educación de Tata McGraw-Hill. ISBN 978-1-259-02993-6.
  • Kirch-Prinz, Ulla; Prinz, Peter (2002). Una guía completa para la programación en C ++ . Jones y Bartlett Learning. ISBN 978-0-7637-1817-6.
  • Conger, David (2006). Creación de juegos en C ++: una guía paso a paso . Nuevos jinetes. ISBN 978-0-7357-1434-2.
  • Skinner, MT (1992). El libro C ++ avanzado . Silicon Press. ISBN 978-0-929306-10-0.
  • Love (1 de septiembre de 2005). Desarrollo del kernel de Linux . Educación Pearson. ISBN 978-81-7758-910-8.
  • DEHURI, SATCHIDANANDA; JAGADEV, ALOK KUMAR; RATH, AMIYA KUMAR (8 de mayo de 2007). PROGRAMACIÓN ORIENTADA A OBJETOS UTILIZANDO C ++ . PHI Learning Pvt. Ltd. ISBN 978-81-203-3085-6.

Enlaces externos [ editar ]

  • Funciones en línea con GNU Compiler Collection (GCC)