functions comando c inline c99

comando - ¿Es "útil" en línea sin "estático" o "externo" útil en C99?



inline function matlab (3)

Del estándar (ISO / IEC 9899: 1999) en sí mismo:

Apéndice J.2 Comportamiento indefinido

  • ...
  • Una función con enlace externo se declara con un especificador de función en inline , pero no se define también en la misma unidad de traducción (6.7.4).
  • ...

El Comité C99 escribió una Rationale y dice:

6.7.4 Especificadores de función

Una nueva característica de C99: la palabra clave en inline , adaptada de C ++, es un especificador de función que se puede usar solo en declaraciones de funciones. Es útil para optimizaciones de programas que requieren que la definición de una función sea visible en el sitio de una llamada. (Tenga en cuenta que el estándar no intenta especificar la naturaleza de estas optimizaciones).

La visibilidad está garantizada si la función tiene enlace interno, o si tiene un enlace externo y la llamada está en la misma unidad de traducción que la definición externa. En estos casos, la presencia de la palabra clave en inline en una declaración o definición de la función no tiene ningún efecto más allá de indicar una preferencia de que las llamadas de esa función deben optimizarse en preferencia a las llamadas de otras funciones declaradas sin la palabra clave en inline .

La visibilidad es un problema para una llamada de una función con enlace externo donde la llamada se encuentra en una unidad de traducción diferente de la definición de la función. En este caso, la palabra clave en inline permite que la unidad de traducción que contiene la llamada también contenga una definición local o en línea de la función.

Un programa puede contener una unidad de traducción con una definición externa, una unidad de traducción con una definición en línea y una unidad de traducción con una declaración pero sin definición para una función. Las llamadas en la última unidad de traducción usarán la definición externa como de costumbre.

Una definición en línea de una función se considera una definición diferente a la definición externa. Si se produce una llamada a alguna función func con enlace externo donde está visible una definición en línea, el comportamiento es el mismo que si la llamada se realizara a otra función, digamos __func , con enlace interno. Un programa conforme no debe depender de la función a la que se llama. Este es el modelo en línea en el Estándar.

Un programa conforme no debe confiar en la implementación usando la definición en línea, ni puede confiar en la implementación usando la definición externa. La dirección de una función es siempre la dirección correspondiente a la definición externa, pero cuando esta dirección se usa para llamar a la función, se puede usar la definición en línea. Por lo tanto, el siguiente ejemplo podría no comportarse como se esperaba.

inline const char *saddr(void) { static const char name[] = "saddr"; return name; } int compare_name(void) { return saddr() == saddr(); // unspecified behavior }

Como la implementación puede usar la definición en línea para una de las llamadas a saddr y usar la definición externa para la otra, no se garantiza que la operación de igualdad se evalúe a 1 (verdadera). Esto muestra que los objetos estáticos definidos dentro de la definición en línea son distintos de su objeto correspondiente en la definición externa. Esto motivó la restricción contra incluso la definición de un objeto no const de este tipo.

Se agregó la inclusión en línea al Estándar de tal manera que se puede implementar con la tecnología de vinculador existente, y un subconjunto de C99 en línea es compatible con C ++. Esto se logró exigiendo que se especificara exactamente una unidad de traducción que contiene la definición de una función en línea como la que proporciona la definición externa para la función. Debido a que esa especificación consiste simplemente en una declaración que carece de la palabra clave en inline , o contiene tanto en inline como extern , también será aceptada por un traductor de C ++.

Inlinear en C99 extiende la especificación de C ++ de dos maneras. Primero, si una función se declara en inline en una unidad de traducción, no necesita declararse en inline en ninguna otra unidad de traducción. Esto permite, por ejemplo, una función de biblioteca que debe estar dentro de la biblioteca pero disponible solo a través de una definición externa en otro lugar. La alternativa de usar una función de envoltura para la función externa requiere un nombre adicional; y también puede afectar negativamente el rendimiento si un traductor no realiza la sustitución en línea.

Segundo, el requisito de que todas las definiciones de una función en línea sean "exactamente las mismas" se reemplaza por el requisito de que el comportamiento del programa no dependa de si una llamada se implementa con una definición en línea visible, o la definición externa, de un función. Esto permite que una definición en línea se especialice para su uso dentro de una unidad de traducción particular. Por ejemplo, la definición externa de una función de biblioteca puede incluir alguna validación de argumento que no es necesaria para llamadas realizadas desde otras funciones en la misma biblioteca. Estas extensiones ofrecen algunas ventajas; y los programadores que están preocupados por la compatibilidad simplemente pueden cumplir con las reglas más estrictas de C ++.

Tenga en cuenta que no es apropiado que las implementaciones proporcionen definiciones en línea de funciones de biblioteca estándar en los encabezados estándar porque esto puede romper algún código heredado que redeclara las funciones de biblioteca estándar después de incluir sus encabezados. La palabra clave en inline está destinada solo a proporcionar a los usuarios una forma portátil de sugerir la incorporación de funciones. Debido a que los encabezados estándar no necesitan ser portátiles, las implementaciones tienen otras opciones en la línea de:

#define abs(x) __builtin_abs(x)

u otros mecanismos no portátiles para incorporar funciones de biblioteca estándar.

Cuando intento construir este código

inline void f() {} int main() { f(); }

usando la línea de comando

gcc -std=c99 -o a a.c

Obtengo un error de enlazador (referencia indefinida a f ). El error desaparece si utilizo static inline o extern inline lugar de solo inline , o si compilo con -O (así que la función está realmente en línea).

Este comportamiento parece estar definido en el párrafo 6.7.4 (6) del estándar C99:

Si todas las declaraciones de alcance de archivo para una función en una unidad de traducción incluyen el especificador de función en inline sin extern , 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 usar 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 en inline como en el ejemplo anterior solo compila consistentemente si también hay una función externa con el mismo nombre, y nunca sé si se llama a mi propia función o a la función externa.

¿No es este comportamiento completamente tonto? ¿Alguna vez es útil definir una función en inline sin static o extern en C99? ¿Me estoy perdiendo de algo?

Resumen de respuestas

Por supuesto, 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 declaración extern desencadena la generación de código binario visible externamente. Y de hecho no hay uso de inline en un archivo .c, solo es útil en los encabezados.

Como explica el razonamiento del comité C99 citado en la respuesta de Jonathan , en 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 lo vea. Pero como el compilador no está obligado a alinear realmente una función, debe existir una definición externa en alguna parte.


En realidad, esta excelente respuesta también responde a su pregunta, creo:

extern en línea

La idea es que "en línea" se pueda usar en un archivo de encabezado y luego "extern en línea" en un archivo .c. "extern inline" es solo la forma de indicar 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 correspondiente declaración "extern inline" en algún archivo .c para generar realmente el código independiente.


> Obtengo un error de enlazador (referencia indefinida a f )

Funciona aquí: Linux x86-64, GCC 4.1.2. Puede ser un error en tu compilador; No veo nada en el párrafo citado del estándar que prohíbe el programa dado. Tenga en cuenta el uso de if en lugar de iff .

Una definición en línea proporciona una alternativa a una definición externa , que un traductor puede usar para implementar cualquier llamada a la función en la misma unidad de traducción.

Entonces, si conoce el comportamiento de la función f y desea llamarla en un ciclo cerrado, puede copiar y pegar su definición en un módulo para evitar llamadas a funciones; o bien , puede proporcionar una definición que, para los fines del módulo actual, sea equivalente (pero omita la validación de la entrada o cualquier optimización que pueda imaginar). Sin embargo, el escritor del compilador tiene la opción de optimizar el tamaño del programa.