Cuando intento compilar este código
inline void f() {}
int main()
{
f();
}
usando la línea de comando
gcc -std=c99 -o a a.c
Recibo un error del vinculador (referencia indefinida a f
). El error desaparece si uso static inline
o en extern inline
lugar de solo inline
, o si compilo con -O
(por lo que la función está en línea).
Este comportamiento parece estar definido en el párrafo 6.7.4 (6) de la norma C99:
Si todas las declaraciones de alcance de archivo para una función en una unidad de traducción incluyen el
inline
especificador de función sinextern
, entonces la definición en esa unidad de traducción es una definició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.
Si entiendo todo esto correctamente, una unidad de compilación con una función definida inline
como en el ejemplo anterior solo se compila de manera consistente si también hay una función externa con el mismo nombre, y nunca sé si se llama a mi propia función o la función externa.
¿No es este comportamiento completamente tonto? ¿Es útil definir una función inline
sin static
o extern
en C99? ¿Me estoy perdiendo de algo?
Resumen de respuestas
Por supuesto que me estaba perdiendo algo, y el comportamiento no es tonto. :)
Como explica Nemo , la idea es poner la definición de la función
inline void f() {}
en el archivo de encabezado y solo una declaración
extern inline void f();
en el archivo .c correspondiente. Solo la extern
declaración activa la generación de código binario visible externamente. Y, de hecho, no se usa inline
en un archivo .c, solo es útil en los encabezados.
Como explica el razonamiento del comité C99 citado en la respuesta de Jonathan , inline
se trata de optimizaciones del compilador que requieren que la definición de una función sea visible en el sitio de una llamada. Esto solo se puede lograr colocando la definición en el encabezado y, por supuesto, una definición en un encabezado no debe emitir código cada vez que el compilador la ve. Pero dado que el compilador no está obligado a incorporar una función, debe existir una definición externa en alguna parte.
inline
sinstatic
yextern
, sin embargo. Desafortunadamente, ninguno de estos temas se aborda en esa pregunta.Respuestas:
En realidad, esta excelente respuesta también responde a su pregunta, creo:
¿Qué hace extern inline?
La idea es que "en línea" se puede usar en un archivo de encabezado y luego "en línea externa" en un archivo .c. "extern inline" es simplemente cómo le indica al compilador qué archivo de objeto debe contener el código generado (visible externamente).
[actualización, para elaborar]
No creo que haya ningún uso para "en línea" (sin "estático" o "externo") en un archivo .c. Pero en un archivo de encabezado tiene sentido, y requiere una declaración "externa en línea" correspondiente en algún archivo .c para generar realmente el código independiente.
fuente
inline void f() {}
en el encabezado yextern inline void f();
en el archivo .c? Entonces, ¿la definición de función real va en el encabezado y el archivo .c contiene una mera declaración en este caso, en orden inverso al habitual?inline
sinstatic
oextern
. Por supuesto questatic inline
está bien, pero de eso no se trata esta pregunta y respuesta.-std=c99
lugar de-std=gnu89
.Del mismo estándar (ISO / IEC 9899: 1999):
El Comité C99 escribió una Justificación y dice:
fuente
> Recibo un error del vinculador (referencia indefinida af
)Funciona aquí: Linux x86-64, GCC 4.1.2. Puede ser un error en su compilador; No veo nada en el párrafo citado del estándar que prohíba el programa dado. Tenga en cuenta el uso de if en lugar de iff .Por lo tanto, si conoce el comportamiento de la función
f
y desea llamarla en un bucle cerrado, puede copiar y pegar su definición en un módulo para evitar llamadas a funciones; o , puede proporcionar una definición que, para los propósitos del módulo actual, sea equivalente (pero omite la validación de entrada o cualquier optimización que pueda imaginar). El escritor del compilador, sin embargo, tiene la opción de optimizar el tamaño del programa.fuente