¿Por qué declarar una función C como estática en línea?

6 minutos de lectura

Encontré un ejemplo para una función C declarada como:

static inline CGPoint SOCGPointAdd(const CGPoint a, const CGPoint b) {
    return CGPointMake(a.x + b.x, a.y + b.y);
}

Hasta ahora, declaraba funciones C de utilidad en archivos .h y las implementaba en archivos .m, así:

CGPoint SOCGPointAdd(const CGPoint a, const CGPoint b) {
    return CGPointMake(a.x + b.x, a.y + b.y);
}

Puedo usar esta función “en línea” en cualquier lugar que desee y también debe ser “estática” porque no está asociada con ningún objeto, como un método Objective-c. ¿Cuál es el punto/ventaja de especificar “estático” y “en línea”?

  • Esta publicación podría responder a su pregunta stackoverflow.com/questions/145838/…

    – usuario376507

    17 de febrero de 2014 a las 17:38

  • eche un vistazo a esta respuesta (a una pregunta ligeramente diferente) stackoverflow.com/a/7767858/464988

    – MByD

    17/02/2014 a las 17:40

avatar de usuario
Eric Pospischil

inline no significa que pueda usar la función “en línea” (es normal usar funciones dentro de otras funciones; no necesita inline para eso); alienta al compilador a construir la función en el código donde se usa (generalmente con el objetivo de mejorar la velocidad de ejecución).

static significa que el nombre de la función no está vinculado externamente. Si la función no fuera declarada static, se requiere que el compilador lo haga visible externamente, para que pueda vincularse con otros módulos de objetos. Para hacer esto, el compilador debe incluir una instancia separada no en línea de la función. Al declarar la función staticestá permitiendo que todas sus instancias estén integradas en el módulo actual, posiblemente sin dejar ninguna instancia separada.

static inline generalmente se usa con funciones pequeñas que se realizan mejor en la rutina de llamada que usando un mecanismo de llamada, simplemente porque son tan cortas y rápidas que realmente es mejor hacerlas que llamar a una copia separada. P.ej:

static inline double square(double x) { return x*x; }

  • A módulo es una unidad de compilación: un archivo fuente más todos los archivos de encabezado que incluye. Cuando compila varios módulos por separado, deben vincularse en un programa. La manera static se utiliza proviene en parte de la historia de la lengua. Si hoy lo estuviéramos redefiniendo desde cero, podríamos llamar a este uso internal en cambio. El concepto en cuestión se llama enlace. Los identificadores en C pueden tener tres tipos de enlace: externo, interno y ninguno. Un identificador con vinculación externa se puede utilizar en varios módulos y, debido a que están vinculados, el identificador hará referencia…

    –Eric Postpischil

    17 de febrero de 2014 a las 18:03

  • … al mismo objeto en diferentes módulos. Un identificador con enlace interno se refiere a un objeto solo dentro de un módulo. Si usa el mismo identificador en un módulo diferente, se refiere a un objeto diferente; no están vinculados para referirse al mismo objeto. (Por cierto, lo que aquí llamo un módulo es a lo que el estándar C se refiere como un unidad de traducción.) Un identificador con enlace interno se puede usar en múltiples funciones dentro de un módulo para referirse al mismo objeto. Luego están los identificadores sin vinculación. Hacen referencia a un objeto solo dentro del bloque en el que se declaran.

    –Eric Postpischil

    17 de febrero de 2014 a las 18:05

  • Las reglas sobre lo que tienen los identificadores de vinculación son un poco complicadas debido a la historia. Aproximadamente, los identificadores declarados en el ámbito del archivo tienen un vínculo externo de forma predeterminada. agregando static los cambia a enlace interno. Los identificadores declarados en el alcance del bloque (dentro de las funciones) no tienen vinculación por defecto. agregando static también los cambia a enlace interno. Aquí hay una complicación: Agregar extern en cambio, cambia una declaración en el alcance del bloque para hacer referencia a una declaración previamente visible en lugar de no tener ningún vínculo. Esa declaración previamente visible podría haber staticResultando en…

    –Eric Postpischil

    17 de febrero de 2014 a las 18:07

  • … una declaración marcada extern refiriéndose a un identificador con enlace interno. Además de eso, static no solo cambia el enlace de los identificadores declarados en bloques, sino que también cambia la clase de almacenamiento de sus objetos (cuando los objetos se crean y se destruyen).

    –Eric Postpischil

    17 de febrero de 2014 a las 18:09

  • Corrección: Identificadores declarados con static en el alcance del bloque todavía no tienen vinculación, no vinculación interna. Solo en el alcance del archivo lo hace static impartir vinculación interna.

    –Eric Postpischil

    17 de febrero de 2014 a las 18:56

avatar de usuario
Merlevède

Si la clase de almacenamiento es externa, el identificador tiene un enlace externo y la definición en línea también proporciona la definición externa. Si la clase de almacenamiento es estáticoel identificador tiene un enlace interno y la definición en línea es invisible en otras unidades de traducción.

Al declarar una función en línea, puede indicarle al compilador que integre el código de esa función en el código de sus llamadores (para reemplazar el código completo de esa función directamente en el lugar desde donde fue llamado). Esto hace que la ejecución sea más rápida al eliminar la sobrecarga de llamadas a funciones. Es por eso que las funciones en línea deben ser muy cortas.

  • Estoy un poco oxidado en mi Obj-C, pero pensé que una estática de C no tenía mucho que ver con las clases de Obj-C y los métodos estáticos (de clase), especialmente porque esta es una función, no un método.

    – José

    17/02/2014 a las 17:40


avatar de usuario
lewis kelsey

Cía, inline significa que es una definición en línea. No tiene enlace interno, tiene no enlace. Nunca llega al enlazador, lo que significa que si el compilador no usa esa definición en línea para alinear cada una de las referencias a la función en la unidad de compilación, habrá un error del enlazador local si un símbolo con el mismo nombre (C usa identificadores no manipulados) con enlace externo no es exportado por otra unidad de traducción en la compilación. La inserción real de referencias a la función por parte del compilador está controlada exclusivamente por el indicador de optimización o __attribute__((always_inline))

No hay diferencia entre static inline y staticambos no alinean la función y proporcionan la función en la salida del ensamblado en -O0 como un símbolo de enlace interno al enlazador, y tanto alinean como optimizan la inclusión de la función en la salida del ensamblado en -O1. static inline tiene una peculiaridad en el sentido de que puede usar un prototipo en línea no estático antes, excepto que este prototipo se ignora y no se usa como una declaración directa (pero usar un prototipo no estático antes de una función estática es un error).

  • inline (GCC -std=gnu90 / gnu89 por defecto) / extern inline (GCC 5.0 en adelante, que utiliza -std=gnu11): Esta es una definición en línea solo del compilador. La emitancia de función visible externamente (en la salida del ensamblado para el uso del ensamblador y el enlazador) para esta definición en línea no ocurre. Si todas las referencias a la función en el archivo no están realmente insertadas por el compilador (y la integración ocurre en niveles de optimización más altos o si usa __attribute__((always_inline)) inline float func()), habrá un error del enlazador local si el compilador no emite la definición externa al enlazador (y si otra unidad de traducción no exporta un símbolo con el mismo nombre con enlace externo). Esto permite que una definición en línea y una función fuera de línea del mismo símbolo se definan por separado, una en línea y la otra fuera de línea, pero no en la misma unidad de traducción, ya que el compilador las confundirá, y una definición fuera de línea será tratada como un error de redefinición. Las definiciones en línea solo son visibles para el compilador y cada unidad de traducción puede tener las suyas propias. Las definiciones en línea no se pueden exportar a otros archivos porque las definiciones en línea no llegan a la etapa de vinculación. Para lograr esto en tiempo de compilación, la definición en línea puede estar en un archivo de encabezado e incluirse en cada unidad de traducción. Esto significa que el uso de inline es una directiva del compilador y extern/static se refiere a la versión fuera de línea producida para el enlazador. Si la función no está definida en la unidad de traducción, no se puede insertar porque se deja en manos del enlazador. Si la función está definida pero no en línea, entonces el compilador usará esta versión si decide en línea

  • extern inline (CCG inline (GCC >5.0): se emite una función visible externamente para esta definición en línea, independientemente de si está en línea o no, lo que significa que este especificador solo se puede usar en una de las unidades de traducción. Esto es intuitivamente lo opuesto a ‘externo’

  • static inline: el compilador emite una función fuera de línea visible localmente a la salida del ensamblado con una directiva local para el ensamblador para esta definición en línea del compilador, pero puede optimizarse en niveles de optimización más altos si todas las funciones pueden estar en línea; nunca permitirá que se produzca un error del enlazador. Se comporta de forma idéntica a static porque el compilador alineará el static definición en niveles de optimización más altos al igual que static inline.

  • Un inline función que no es static no debe contener variables de duración de almacenamiento estáticas no constantes ni acceder a variables estáticas de ámbito de archivo, esto generará una advertencia del compilador. Esto se debe a que las versiones en línea y fuera de línea de la función tendrán distintas variables estáticas si la versión fuera de línea se proporciona desde una unidad de traducción diferente. El compilador puede alinear algunas funciones, no emitir un símbolo local para vincularlo a esas referencias y dejar el enlace al vinculador que podría encontrar un símbolo de función externo, que se supone que es la misma función ya que tiene el mismo identificador. Por lo tanto, le recuerda al programador que lógicamente debería ser constante porque modificar y leer la estática dará como resultado un comportamiento indefinido; si el compilador inserta esta referencia de función, leerá un valor estático nuevo en la función en lugar del escrito en una llamada anterior a la función, donde esa referencia anterior a la función no estaba en línea, por lo tanto, la variable que se escribió en la convocatoria anterior habría sido proporcionado por una unidad de traducción diferente. En este caso, da como resultado una copia local para cada unidad de traducción y una copia global y no está definido a qué copia se accede. Hacerlo constante asegura que todas las copias sean idénticas y nunca cambien entre sí, haciendo que el comportamiento sea definido y conocido.

  • Usando un inline / extern inline prototipo antes/después de una definición no en línea significa que el prototipo se ignora.

  • Usando un inline prototipo antes de una definición en línea es cómo crear un prototipo de una función en línea sin efectos secundarios, declarando un prototipo en línea después de que la definición en línea no cambie nada a menos que cambie el especificador de almacenamiento.

  • Usando un extern inline / extern / prototipo normal antes/después de que una definición en línea sea idéntica a una extern inline definición; es una sugerencia que proporciona una definición externa fuera de línea de la función, utilizando la definición en línea.

  • Usando extern inline / inline en un prototipo sin una definición en el archivo pero se hace referencia en el archivo da como resultado inline siendo ignorado y luego se comporta como un prototipo regular (extern / regular, que son idénticos)

  • Usando un static inline / static en un prototipo sin una definición en el archivo pero se hace referencia en el archivo da como resultado un enlace correcto y un uso de tipo correcto pero una advertencia del compilador que dice que la función con enlace interno no se ha definido (por lo que usa una definición externa)

  • Usando un / regular extern / extern inline prototipo antes de un static inline o static la definición es una ‘declaración estática de ‘func’ sigue un error de declaración no estática’; usarlo después no hace nada y se ignoran. Usando un static o static inline prototipo antes/después de un static inline Se permite la definición. Usando un inline prototipo antes de un static inline La definición se ignora y no actuará como una declaración hacia adelante. Esta es la única forma en que static inline difiere de static como un prototipo normal antes de un static La definición da como resultado un error, pero esto no.

  • Usando un static inline prototipo antes de un regular / extern / static / static inline / extern inline la definición da como resultado static inline anulando los especificadores y actúa tan correctamente como una declaración directa.

  • __attribute__((always_inline)) siempre inserta el símbolo de función en la unidad de traducción y usa esta definición. El atributo solo se puede utilizar en definiciones. Los especificadores de almacenamiento/en línea no se ven afectados por esto y se pueden usar con él.

  • extern inline (GCC 5.0 en adelante, que utiliza -std=gnu11)” Ahora, ¿eso es un cambio GNU11 o un cambio C11?

    – mojar

    15 de febrero de 2021 a las 17:04

  • @dyp aparentemente este cambio ocurre tanto en c99 como en gnu99

    –Lewis Kelsey

    21 de febrero de 2021 a las 10:43

Las funciones en línea son para definir en archivos de encabezado. Las funciones pequeñas se definen en archivos de encabezado. Debe ser estático para que solo pueda acceder a miembros estáticos.

¿Ha sido útil esta solución?