Cómo usar correctamente la palabra clave externa en C


236

Mi pregunta es sobre cuándo se debe hacer referencia a una función con la externpalabra clave en C.

No veo cuándo se debe usar esto en la práctica. Mientras escribo un programa, todas las funciones que uso están disponibles a través de los archivos de encabezado que he incluido. Entonces, ¿por qué sería útil externobtener acceso a algo que no estaba expuesto en el archivo de encabezado?

Podría estar pensando en cómo externfunciona incorrectamente, y si es así, corrígeme.

Editar: ¿Debería hacer externalgo cuando es la declaración predeterminada sin la palabra clave en un archivo de encabezado?


Respuestas:


290

" extern" cambia el enlace. Con la palabra clave, se supone que la función / variable está disponible en otro lugar y la resolución se difiere al vinculador.

Hay una diferencia entre "externo" en las funciones y en las variables: en las variables no crea una instancia de la variable en sí misma, es decir, no asigna memoria. Esto debe hacerse en otro lugar. Por lo tanto, es importante si desea importar la variable desde otro lugar. Para funciones, esto solo le dice al compilador que el enlace es externo. Como este es el valor predeterminado (usa la palabra clave "estática" para indicar que una función no está vinculada mediante un enlace externo), no necesita usarla explícitamente.


1
entonces ¿por qué lo mismo extern hay en Git: un software muy popular y moderna comprobarlo: github.com/git/git/blob/master/strbuf.h
rsjethani

K&R no tiene en cuenta que es predeterminado declarar la función como "externa", sin embargo, esta respuesta resuelve mi confusión.
acgtyrant

@rsjethani Creo que es para hacer el documento más estricto y el formato.
acgtyrant

Tal vez una pregunta tonta, pero ¿cómo se compara esto con la declaración directa?
weberc2

197

extern le dice al compilador que estos datos se definen en algún lugar y se conectarán con el enlazador.

Con la ayuda de las respuestas aquí y hablar con algunos amigos aquí es el ejemplo práctico de un uso de extern .

Ejemplo 1 - para mostrar una trampa:

File stdio.h:

int errno;
/* other stuff...*/

myCFile1.c:
#include <stdio.h>

Code...

myCFile2.c:
#include <stdio.h>

Code...

Si myCFile1.o y myCFile2.o están vinculados, cada uno de los archivos c tiene copias separadas de errno . Este es un problema ya que se supone que el mismo error está disponible en todos los archivos vinculados.

Ejemplo 2 - La solución.

File stdio.h:

extern int errno;
/* other stuff...*/

File stdio.c

int errno;

myCFile1.c:
#include <stdio.h>

Code...

myCFile2.c:
#include <stdio.h>

Code...

Ahora, si myCFile1.o y MyCFile2.o están vinculados por el vinculador, ambos apuntarán al mismo error . Así, resolviendo la implementación con extern .


71
El problema no es que los módulos myCFile1 y myCFile2 tengan una copia separada de errno, es que ambos están exponiendo un símbolo llamado "errno". Cuando el enlazador ve esto, no sabe qué "errno" elegir, por lo que saldrá con un mensaje de error.
cwick 02 de

2
¿Qué significa "enlazado por el enlazador" en realidad? todos usan este término, no encuentro ninguna definición :(
Marcel Falliere

77
@MarcelFalliere Wiki ~ Compiler compila cada archivo fuente por sí mismo y crea un archivo objeto para cada archivo fuente. Linker vincula estos archivos de objeto a 1 ejecutable.
Bitterblue

1
@cwick gcc no está dando un error o advertencia incluso después de usar -Wally -pedantic. Por qué ? y cómo ?
b-ak

66
¿No incluye un protector de protección contra esta cosa exacta?
obskyr

32

Ya se ha dicho que la externpalabra clave es redundante para las funciones.

En cuanto a las variables compartidas entre las unidades de compilación, debe declararlas en un archivo de encabezado con la palabra clave externa, luego definirlas en un único archivo fuente, sin la palabra clave externa. El archivo de origen único debe ser el que comparte el nombre del archivo de encabezado, para la mejor práctica.


@aib "redundante para funciones", verifique mi comentario en la respuesta de bluebrother.
rsjethani

¿Qué sucede si no desea exponer ninguna de las funciones en el archivo de encabezado? ¿No sería mejor declarar la variable en un archivo C y acceder a ella externamente en otro? deje que el vinculador resuelva el problema y oculte el resto del encabezado.
ste3e

16

Muchos años después, descubro esta pregunta. Después de leer cada respuesta y comentario, pensé que podría aclarar algunos detalles ... Esto podría ser útil para las personas que llegan aquí a través de la búsqueda de Google.

La pregunta es específicamente sobre el uso de funciones "externas", por lo que ignoraré el uso de "externas" con variables globales.

Definamos 3 prototipos de funciones:

//--------------------------------------
//Filename: "my_project.H"
extern int function_1(void);
static int function_2(void);
       int function_3(void);

El archivo de encabezado puede ser utilizado por el código fuente principal de la siguiente manera:

//--------------------------------------
//Filename: "my_project.C"
#include "my_project.H"

void main(void){
    int v1 = function_1();
    int v2 = function_2();
    int v3 = function_3();
}

int function_2(void) return 1234;

Para compilar y vincular, debemos definir "function_2" en el mismo archivo de código fuente donde llamamos a esa función. Las otras dos funciones se pueden definir en un código fuente diferente " .C" o se pueden ubicar en cualquier archivo binario ( .OBJ, * .LIB, * .DLL), para el cual es posible que no tengamos el código fuente.

Incluyamos nuevamente el encabezado "my_project.H" en un archivo "* .C" diferente para comprender mejor la diferencia. En el mismo proyecto, agregamos el siguiente archivo:

//--------------------------------------
//Filename: "my_big_project_splitted.C"
#include "my_project.H"

void old_main_test(void){
    int v1 = function_1();
    int v2 = function_2();
    int v3 = function_3();
}

int function_2(void) return 5678;

int function_1(void) return 12;
int function_3(void) return 34;

Características importantes a tener en cuenta:

  • Cuando una función se define como "estática" en un archivo de encabezado, el compilador / vinculador debe encontrar una instancia de una función con ese nombre en cada módulo que utiliza ese archivo de inclusión.

  • Una función que forma parte de la biblioteca C puede reemplazarse en un solo módulo redefiniendo un prototipo con "estático" solo en ese módulo. Por ejemplo, reemplace cualquier llamada a "malloc" y "gratis" para agregar la función de detección de pérdida de memoria.

  • El especificador "extern" no es realmente necesario para las funciones. Cuando no se encuentra "estático", siempre se supone que una función es "externa".

  • Sin embargo, "extern" no es el valor predeterminado para las variables. Normalmente, cualquier archivo de encabezado que defina variables para que sean visibles en muchos módulos debe usar "extern". La única excepción sería si se garantiza que un archivo de encabezado se incluya desde un solo módulo.

    Muchos administradores de proyectos requerirían que dicha variable se coloque al comienzo del módulo, no dentro de ningún archivo de encabezado. Algunos proyectos grandes, como el emulador de videojuegos "Mame", incluso requieren que dicha variable aparezca solo por encima de la primera función que los usa.


Entonces, ¿por qué exactamente una función estática necesita una definición frente a las externas? (Sé que esto lleva 2 años de retraso, pero en realidad es realmente útil para comprender)
SubLock69

2
La definición es necesaria si llama a la función en la línea 100 y la instancia en la línea 500. La línea 100 declararía un prototipo indefinido. Entonces, agregas el prototipo cerca de la parte superior.
Christian Gingras

15

En C, 'extern' está implícito para los prototipos de funciones, ya que un prototipo declara una función que se define en otro lugar. En otras palabras, un prototipo de función tiene un enlace externo por defecto; usar 'extern' está bien, pero es redundante.

(Si se requiere un enlace estático, la función debe declararse como 'estática' tanto en su prototipo como en el encabezado de la función, y estos normalmente deberían estar en el mismo archivo .c).


8

Un muy buen artículo sobre la externpalabra clave, junto con los ejemplos: http://www.geeksforgeeks.org/understanding-extern-keyword-in-c/

Aunque no estoy de acuerdo en que usar externdeclaraciones en funciones sea redundante. Se supone que esta es una configuración del compilador. Por lo tanto, recomiendo usar las externdeclaraciones en la función cuando sea necesario.


3
Leí el artículo de geeksforgeeks.org antes de venir aquí, pero lo encontré bastante mal escrito. Además de las deficiencias gramaticales y de sintaxis, utiliza muchas palabras para señalar el mismo punto varias veces y luego roza información crítica. Por ejemplo, en el Ejemplo 4, de repente se incluye 'somefile.h', pero no se dice nada más que: "Suponiendo que somefile.h tiene la definición de var". Bueno, la información que estamos "suponiendo" es la información que estoy buscando. Desafortunadamente, ninguna de las respuestas en esta página es mucho mejor.
Elise van Looij

6

Si cada archivo de su programa se compila primero en un archivo de objeto, entonces los archivos de objeto se vinculan entre sí, es necesario extern. Le dice al compilador "Esta función existe, pero su código está en otro lugar. No se asuste".


Um, así es como normalmente se hace la traducción: los archivos de origen se compilan en archivos de objetos y luego se vinculan. ¿Cuándo no necesitarías extern en ese caso? Tampoco usaría #include para obtener funciones, sino prototipos de funciones. No entiendo de qué estás hablando.
David Thornley

Parece que últimamente tengo este problema de leer mal las cosas. Lo siento por eso. Cuando era nuevo en C, incluiría "file.c" para incluir las funciones en un archivo directamente en el otro archivo. Entonces descubrí cómo usar 'externo'. Pensé que estaba cometiendo el mismo error que yo.
Chris Lutz

4

Todas las declaraciones de funciones y variables en los archivos de encabezado deben ser extern.

Las excepciones a esta regla son las funciones en línea definidas en el encabezado y las variables que, aunque definidas en el encabezado, tendrán que ser locales para la unidad de traducción (el archivo fuente en el que se incluye el encabezado): estas deberían ser static.

En los archivos de origen, externno debe usarse para funciones y variables definidas en el archivo. Simplemente prefije las definiciones locales con staticy no haga nada para las definiciones compartidas: serán símbolos externos de forma predeterminada.

La única razón para usar externun archivo fuente es declarar funciones y variables que están definidas en otros archivos fuente y para las cuales no se proporciona ningún archivo de encabezado.


Declarar prototipos de funciones externes realmente innecesario. A algunas personas no les gusta porque solo desperdiciará espacio y las declaraciones de funciones ya tienden a desbordar los límites de la línea. A otros les gusta porque de esta manera, las funciones y las variables se pueden tratar de la misma manera.


¿Puede dar una razón por la cual "Todas las declaraciones de funciones y variables en los archivos de encabezado deben ser externas"? Según las otras respuestas, me parece que son externas por defecto.
lillq

@Lane: externes opcional para las declaraciones de funciones, pero me gusta tratar las variables y las funciones de la misma manera, al menos eso es lo más razonable que se me ocurre, ya que no recuerdo exactamente por qué comencé a hacer esto;)
Christoph

¿No es una mejor idea incluir siempre variables globales en el archivo C para que otros archivos C aleatorios que incluyan el encabezado no las vean? Y para usar siempre extern en cada global excepto el sumidero verdadero inicializado como una cuestión de claridad; si tiene el prefijo externo, entonces se define en otra parte.
ste3e

3

Las funciones realmente definidas en otros archivos fuente solo deben declararse en encabezados. En este caso, debe usar extern al declarar el prototipo en un encabezado.

La mayoría de las veces, sus funciones serán una de las siguientes (más como una mejor práctica):

  • estática (funciones normales que no son visibles fuera de ese archivo .c)
  • en línea estática (en línea desde archivos .c o .h)
  • extern (declaración en encabezados del siguiente tipo (ver más abajo))
  • [sin palabra clave alguna] (funciones normales destinadas a acceder mediante declaraciones externas)

¿Por qué externarías al declarar el prototipo si este es el valor predeterminado?
lillq

@Lane: Puede ser un poco parcial, pero cada proyecto en el que he trabajado utiliza la siguiente convención: en los encabezados, declara prototipos solo para funciones externas (por lo tanto, externas). En los archivos .c, se pueden usar prototipos simples para obviar la necesidad de un pedido específico, pero no se deben colocar en encabezados.
Eduard - Gabriel Munteanu

1

Cuando tiene esa función definida en un dll o lib diferente, de modo que el compilador difiere al enlazador para encontrarlo. El caso típico es cuando está llamando a funciones desde la API del sistema operativo.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.