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”?
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 static
está 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 usointernal
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. agregandostatic
también los cambia a enlace interno. Aquí hay una complicación: Agregarextern
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 haberstatic
Resultando 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 hacestatic
impartir vinculación interna.–Eric Postpischil
17 de febrero de 2014 a las 18:56
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
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 static
ambos 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 astatic
porque el compilador alineará elstatic
definición en niveles de optimización más altos al igual questatic inline
. -
Un
inline
función que no esstatic
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 unaextern 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 resultadoinline
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 unstatic inline
ostatic
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 unstatic
ostatic inline
prototipo antes/después de unstatic inline
Se permite la definición. Usando uninline
prototipo antes de unstatic inline
La definición se ignora y no actuará como una declaración hacia adelante. Esta es la única forma en questatic inline
difiere destatic
como un prototipo normal antes de unstatic
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 resultadostatic 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.
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