Efectos de la palabra clave extern sobre las funciones C

En C, no noté ningún efecto de la palabra clave extern utilizada antes de la statement de la función. Al principio, pensé que al definir extern int f(); en un solo archivo te obliga a implementarlo fuera del scope del archivo. Sin embargo, descubrí que ambos:

 extern int f(); int f() {return 0;} 

y

 extern int f() {return 0;} 

comstackr bien, sin advertencias de gcc. gcc -Wall -ansi ; ni siquiera aceptaría // comentarios.

¿Hay algún efecto para utilizar las definiciones extern antes de las funciones ? O solo es una palabra clave opcional sin efectos secundarios para las funciones.

En este último caso, no entiendo por qué los diseñadores estándar eligieron ensuciar la gramática con palabras clave superfluas.

EDITAR: para aclarar, sé que hay uso para las variables extern , pero solo estoy preguntando por funciones extern .

Tenemos dos archivos, foo.c y bar.c.

Aquí está foo.c

 #include  volatile unsigned int stop_now = 0; extern void bar_function(void); int main(void) { while (1) { bar_function(); stop_now = 1; } return 0; } 

Ahora, aquí está bar.c

 #include  extern volatile unsigned int stop_now; void bar_function(void) { while (! stop_now) { printf("Hello, world!\n"); sleep(30); } } 

Como puede ver, no tenemos un encabezado compartido entre foo.c y bar.c, sin embargo bar.c necesita algo declarado en foo.c cuando está vinculado, y foo.c necesita una función de bar.c cuando está enlazado.

Al usar ‘extern’, le está diciendo al comstackdor que lo que le sigue se encontrará (no estático) en el momento del enlace; no reserve nada en el pase actual, ya que se encontrará más tarde. Las funciones y las variables se tratan por igual a este respecto.

Es muy útil si necesita compartir algo global entre módulos y no desea ponerlo / inicializarlo en un encabezado.

Técnicamente, cada función en un encabezado público de la biblioteca es ‘extern’, sin embargo etiquetarlas como tal tiene muy poco o ningún beneficio, dependiendo del comstackdor. La mayoría de los comstackdores pueden darse cuenta por sí mismos. Como puede ver, esas funciones en realidad están definidas en otro lugar.

En el ejemplo anterior, main () imprimirá hello world solo una vez, pero continuará ingresando bar_function (). También tenga en cuenta que bar_function () no va a regresar en este ejemplo (ya que es solo un ejemplo). Imagínese que stop_now se modificó cuando se repara una señal (por lo tanto, volátil) si esto no parece lo suficientemente práctico.

Los complementos son muy útiles para cosas como manejadores de señal, un mutex que no quieres poner en un encabezado o estructura, etc. La mayoría de los comstackdores optimizarán para asegurar que no reserven memoria para objetos externos, ya que saben que Lo reservaré en el módulo donde se define el objeto. Sin embargo, una vez más, no tiene mucho sentido especificarlo con comstackdores modernos al crear prototipos de funciones públicas.

Espero que ayude 🙂

Hasta donde recuerdo el estándar, todas las declaraciones de funciones se consideran como “externas” de forma predeterminada, por lo que no es necesario especificarlas explícitamente.

Eso no hace que esta palabra clave sea inútil, ya que también se puede usar con variables (y en ese caso, es la única solución para resolver problemas de vinculación). Pero con las funciones, sí, es opcional.

Debe distinguir entre dos conceptos separados: definición de función y statement de símbolo. “extern” es un modificador de enlace, una sugerencia para el comstackdor acerca de dónde se define el símbolo al que se hace referencia después (la sugerencia es “no aquí”).

Si escribo

 extern int i; 

en el scope del archivo (fuera de un bloque de funciones) en un archivo C, entonces está diciendo “la variable puede definirse en otro lugar”.

 extern int f() {return 0;} 

es tanto una statement de la función f como una definición de la función f. La definición en este caso prevalece sobre la externa.

 extern int f(); int f() {return 0;} 

primero es una statement, seguida de la definición.

El uso de extern es incorrecto si quiere declarar y definir simultáneamente una variable de scope de archivo. Por ejemplo,

 extern int i = 4; 

dará un error o advertencia, según el comstackdor.

El uso de extern es útil si explícitamente desea evitar la definición de una variable.

Dejame explicar:

Digamos que el archivo ac contiene:

 #include "ah" int i = 2; int f() { i++; return i;} 

El archivo ah incluye:

 extern int i; int f(void); 

y el archivo bc contiene:

 #include  #include "ah" int main(void){ printf("%d\n", f()); return 0; } 

El extern en el encabezado es útil, porque le dice al comstackdor durante la fase de enlace, “esta es una statement, y no una definición”. Si elimino la línea en ac que define i, le asigna espacio y le asigna un valor, el progtwig no puede comstackr con una referencia indefinida. Esto le dice al desarrollador que se ha referido a una variable, pero aún no la ha definido. Si, por otro lado, omito la palabra clave “extern” y elimino la línea int i = 2 , el progtwig aún se comstack: se definirá con un valor predeterminado de 0.

Las variables de ámbito de archivo se definen implícitamente con un valor predeterminado de 0 o NULL si no les asigna un valor explícitamente, a diferencia de las variables de bloque de ámbito que declara en la parte superior de una función. La palabra clave extern evita esta definición implícita y, por lo tanto, ayuda a evitar errores.

Para funciones, en declaraciones de función, la palabra clave es de hecho redundante. Las declaraciones de funciones no tienen una definición implícita.

La palabra clave extern toma diferentes formas dependiendo del entorno. Si hay una statement disponible, la palabra clave extern toma el enlace como el especificado anteriormente en la unidad de traducción. En ausencia de tal statement, extern especifica un enlace externo.

 static int g(); extern int g(); /* g has internal linkage */ extern int j(); /* j has tentative external linkage */ extern int h(); static int h(); /* error */ 

Aquí están los párrafos relevantes del borrador C99 (n1256):

6.2.2 Enlaces de identificadores

[…]

4 Para un identificador declarado con el especificador de especificación de clase de almacenamiento en un scope en el que es visible una statement previa de ese identificador, 23) si la statement previa especifica una vinculación interna o externa, la vinculación del identificador en la statement posterior es la misma como el enlace especificado en la statement previa. Si no hay una statement previa visible, o si la statement previa no especifica ningún vínculo, entonces el identificador tiene una vinculación externa.

5 Si la statement de un identificador para una función no tiene un especificador de clase de almacenamiento, su vinculación se determina exactamente como si se hubiera declarado con el especificador de clase de almacenamiento extern. Si la statement de un identificador para un objeto tiene un scope de archivo y no un especificador de clase de almacenamiento, su vinculación es externa.

Las funciones en línea tienen reglas especiales sobre lo que significa extern . (Tenga en cuenta que las funciones en línea son una extensión C99 o GNU, no estaban en la C.

Para funciones que no son en línea, extern no es necesario ya que está activado por defecto.

Tenga en cuenta que las reglas para C ++ son diferentes. Por ejemplo, se necesita una extern "C" en la statement C ++ de las funciones C a las que se va a llamar desde C ++, y existen reglas diferentes sobre inline .

La palabra clave extern informa al comstackdor de que la función o variable tiene una vinculación externa; en otras palabras, que es visible desde archivos distintos a aquel en el que está definido. En este sentido, tiene el significado opuesto a la palabra clave static . Es un poco extraño poner extern en el momento de la definición, ya que ningún otro archivo tendría visibilidad de la definición (o daría lugar a múltiples definiciones). Normalmente se pone extern en una statement en algún momento con visibilidad externa (como un archivo de encabezado) y se coloca la definición en otro lugar.

declarar una función extern significa que su definición se resolverá en el momento de la vinculación, no durante la comstackción.

A diferencia de las funciones normales, que no están declaradas de manera externa, se puede definir en cualquiera de los archivos fuente (pero no en múltiples archivos fuente, de lo contrario obtendrá un error de enlazador que indica que ha dado múltiples definiciones de la función) incluyendo el de que se declara extern. Así que, en su caso, el enlazador resuelve la definición de la función en el mismo archivo.

No creo que hacer esto sea muy útil, sin embargo, hacer este tipo de experimentos proporciona una mejor comprensión sobre cómo funciona el comstackdor y el enlazador del lenguaje.

La razón por la que no tiene ningún efecto es porque en el tiempo de enlace el enlazador intenta resolver la definición externa (en su caso extern int f() ). No importa si lo encuentra en el mismo archivo o en otro diferente, siempre que se encuentre.

Espero que esto responda a su pregunta.