Pregunta

Estoy intentando reemplazar algunas subrutinas macro con funciones en línea, para que el compilador pueda optimizarlas, para que el depurador pueda intervenir en ellas, etc.Si las defino como funciones normales funciona:

void do_something(void)
{
  blah;
}

void main(void)
{
  do_something();
}

pero si los defino como en línea:

inline void do_something(void)
{
  blah;
}

void main(void)
{
  do_something();
}

dice "Error:Externo indefinido".¿Qué significa eso?Apuñalando en la oscuridad, lo intenté

static inline void do_something(void)
{
  blah;
}

void main(void)
{
  do_something();
}

y no más errores.La definición de la función y la llamada a la función están en el mismo archivo .c.

¿Alguien puede explicar por qué uno funciona y el otro no?

(Segunda pregunta relacionada:¿Dónde pongo funciones en línea si quiero usarlas en más de un archivo .c?)

¿Fue útil?

Solución

Primero, el compilador no siempre incluye funciones marcadas como inline;por ejemplo, si desactiva todas las optimizaciones, probablemente no las incorporará.

Cuando defines una función en línea

inline void do_something(void)
{
  blah
}

y usar esa función, incluso en el mismo archivo, la llamada a esa función la resuelve el vinculador, no el compilador, porque es implícitamente "externo".Pero esta definición por sí sola no proporciona una definición externa de la función.

Si incluye una declaración sin inline

void do_something(void);

en un archivo C que puede ver el inline definición, el compilador proporcionará una definición externa de la función y el error debería desaparecer.

La razón static inline funciona es que hace que la función sea visible solo dentro de esa unidad de compilación y, por lo tanto, permite que el compilador resuelva la llamada a la función (y la optimice) y emita el código para la función dentro de esa unidad de compilación.Entonces el enlazador no tiene que resolverlo, por lo que no hay necesidad de una definición externa.

El mejor lugar para colocar funciones en línea es en un archivo de encabezado y declararlas static inline.Esto elimina la necesidad de una definición externa, por lo que resuelve el problema del vinculador.Sin embargo, esto hace que el compilador emita el código para la función en cada unidad de compilación que lo utiliza, por lo que podría generar un exceso de código.Pero como la función está en línea, probablemente sea pequeña de todos modos, por lo que esto no suele ser un problema.

La otra opción es definir como extern inline en el encabezado y en un archivo C proporcione y extern declaración sin el inline modificador.

El manual de gcc lo explica así:

Al declarar una función en línea, puede dirigir GCC que haga llamadas a esa función más rápido.Una forma en que GCC puede lograr esto es integrar el código de esa función en el código para sus personas que llaman.Esto hace que la ejecución sea más rápida al eliminar la sobrecarga de funciones;Además, si alguno de los valores de argumentos reales es constante, sus valores conocidos pueden permitir simplificaciones en el momento de la compilación para que no se deba incluir todo el código de la función en línea.El efecto sobre el tamaño del código es menos predecible;El código de objeto puede ser mayor o menor con la enlinición de la función, dependiendo del caso particular.También puede dirigir a GCC para intentar integrar todas las funciones "lo suficientemente simples" en sus personas que llaman con la opción -finline-functions.

GCC implementa tres semánticas diferentes de declarar una función en línea.Uno está disponible con -std=gnu89 o -fgnu89-inline o cuando gnu_inline El atributo está presente en todas las declaraciones en línea, otro cuando -std=c99, -std=c1x, -std=gnu99 o -std=gnu1x(sin -fgnu89-inline), y el tercero se utiliza al compilar C++.

Para declarar una función en línea, use el inline Palabra clave en su declaración, como esta:

 static inline int
 inc (int *a)
 {
   return (*a)++;
 }

Si está escribiendo un archivo de encabezado que se incluirá en los programas ISO C90, escriba __inline__ en lugar de inline.

Los tres tipos de inserción se comportan de manera similar en dos casos importantes:cuando el inline palabra clave se utiliza en un static función, como el ejemplo anterior, y cuando una función se declara por primera vez sin usar el inline palabra clave y luego se define con inline, como esto:

 extern int inc (int *a);
 inline int
 inc (int *a)
 {
   return (*a)++;
 }

En ambos casos comunes, el programa se comporta igual que si no hubiera utilizado el inline palabra clave, excepto por su velocidad.

Cuando una función está tanto en línea como static, si todas las llamadas a la función están integradas en la persona que llama, y ​​la dirección de la función nunca se usa, entonces nunca se hace referencia al propio código de ensamblador de la función.En este caso, GCC en realidad no genera código de ensamblador para la función, a menos que especifique la opción -fkeep-inline-functions.Algunas llamadas no pueden integrarse por varias razones (en particular, las llamadas que preceden a la definición de la función no se pueden integrar, y ninguno de los dos puede recursivo dentro de la definición).Si hay una llamada no integrada, entonces la función se compila en el código del ensamblador como de costumbre.La función también debe compilarse como de costumbre si el programa se refiere a su dirección, porque eso no se puede ingresar.

Tenga en cuenta que ciertos usos en una definición de función pueden hacerlo inadecuado para la sustitución en línea.Entre estos usos se encuentran:Uso de varargs, uso de ALLOCA, uso de tipos de datos de tamaño variable, uso de GOTO calculado, uso de GOTO no local y funciones anidadas.Usando -Winline avisará cuando una función marcada inline No se pudo sustituir y dará la razón del fracaso.

Como lo requiere ISO C ++, GCC considera que las funciones miembros definidas dentro del cuerpo de una clase se marcan en línea incluso si no se declaran explícitamente con el inline palabra clave.Puedes anular esto con -fno-default-inline.

GCC no está en línea de ninguna función al no optimizar a menos que especifique el always_inline atributo para la función, así:

 /* Prototype.  */
 inline void foo (const char) __attribute__((always_inline));

El resto de esta sección es específico para la incorporación de GNU C90.

Cuando una función en línea no es static, entonces el compilador debe asumir que puede haber llamadas de otros archivos de origen;Dado que un símbolo global se puede definir solo una vez en cualquier programa, la función no debe definirse en los otros archivos de origen, por lo que las llamadas allí no pueden integrarse.Por lo tanto, un no-static La función en línea siempre se compila por sí sola de la manera habitual.

Si especifica ambos inline y extern En la definición de función, entonces la definición se usa solo para insertar.En ningún caso la función compilada por sí sola, ni siquiera si se refiere a su dirección explícitamente.Tal dirección se convierte en una referencia externa, como si solo hubiera declarado la función y no la hubiera definido.

Esta combinación de inline y extern tiene casi el efecto de una macro.La forma de usarlo es poner una definición de función en un archivo de encabezado con estas palabras clave y poner otra copia de la definición (falta inline y extern) en un archivo de biblioteca.La definición en el archivo de encabezado hará que la mayoría de las llamadas a la función se ingresen.Si queda algún uso de la función, se referirán a la copia única en la biblioteca.

Otros consejos

Para inline Funciones para trabajar con C99 (solo llegaron allí al idioma) Tendría que dar la definición en un archivo de encabezado

inline void do_something(void)
{
  blah
}

y en una Unidad de compilación (también conocido como .c) Usted coloca algún tipo de "instanciación"

void do_something(void);

sin el inline.

Debe colocarlos en un archivo de encabezado si desea usarlos desde varios archivos.

Y para el error de enlazador: la declaración predeterminada de una función implica que es "externo", pero como está en línea, el enlazador puede encontrar el stub de símbolo generado por el compilador, de ahí el error.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top