Cómo usar correctamente la palabra clave extern en C

8 minutos de lectura

avatar de usuario
lilq

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

No veo cuándo debería usarse 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 extern para obtener acceso a algo que no estaba expuesto en el archivo de encabezado?

Podría estar pensando en cómo extern funciona incorrectamente, y si es así por favor corrígeme.

También… ¿Deberías extern algo cuando es la declaración predeterminada sin la palabra clave en un archivo de encabezado?

  • relacionado para funciones: stackoverflow.com/questions/856636/… para varables: stackoverflow.com/questions/1433204

    – Ciro Santilli Путлер Капут 六四事

    19 de junio de 2015 a las 2:16


avatar de usuario
hermano azul

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 delega al enlazador.

Hay una diferencia entre extern sobre funciones y sobre variables.

Para Variables no instancia la variable en sí, es decir, no asigna ninguna 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 (se utiliza la palabra clave static para indicar que una función no está vinculada mediante un enlace externo), no necesita usarla explícitamente.

  • Entonces, ¿por qué hay lo mismo externo en Git: un software muy popular y moderno? Compruébalo: github.com/git/git/blob/master/strbuf.h

    – rsjethani

    11 de agosto de 2013 a las 14:11

  • K&R no nota que es predeterminado declarar la función como “externa”, sin embargo, ¡esta respuesta resuelve mi confusión!

    – actyrant

    22 de febrero de 2014 a las 8:12

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

    – actyrant

    22 de febrero de 2014 a las 8:21

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

    – weberc2

    16 de noviembre de 2015 a las 15:13

avatar de usuario
lilq

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

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

Ejemplo 1 – para mostrar una trampa:

stdio.h:

int errno;

myCFile1.c:

#include <stdio.h>

// Code using errno...

myCFile2.c:

#include <stdio.h>

// Code using errno...

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

Ejemplo 2 – La solución.

stdio.h:

extern int errno;

stdio.c:

int errno;

myCFile1.c:

#include <stdio.h>

// Code using errno...

myCFile2.c:

#include <stdio.h>

// Code using errno...

ahora si los dos myCFile1.o y MyCFile2.o están vinculados por el enlazador, ambos apuntarán al mismo errno. Así, resolviendo la implementación con extern.

  • El problema no es que los módulos myCFile1 y myCFile2 tengan una copia separada de errno, es que ambos exponen un símbolo llamado “errno”. Cuando el enlazador ve esto, no sabe qué “errno” elegir, por lo que se recuperará con un mensaje de error.

    – cwick

    2 de febrero de 2009 a las 16:52

  • ¿Qué significa realmente “vinculado por el enlazador”? todos usan este término, no encuentro ninguna definición 🙁

    – Marcel Falliere

    1 de octubre de 2013 a las 6:59

  • @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.

    – Azul amargo

    21 de noviembre de 2013 a las 7:50


  • ¿Un protector incluido no protege contra esto exactamente?

    – obskyr

    20 de abril de 2017 a las 14:06

  • @obskyr no, incluir guardias no protegerá contra esto. Los protectores de inclusión solo evitan que el mismo archivo de encabezado se incluya varias veces en un solo archivo de origen. No impide que ese encabezado aparezca en varios archivos de origen. Entonces, aún tendría el problema de que varias fuentes definieron la misma variable.

    – iheanyi

    4 de marzo de 2021 a las 19:11

avatar de usuario
aib

Ya se ha dicho que la extern la palabra 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 extern y luego definirlas en un solo archivo fuente, sin la palabra clave extern. El único archivo de origen debe ser el que comparte el nombre del archivo de encabezado, como práctica recomendada.

  • @aib “redundante para funciones”, revisa mi comentario en la respuesta de bluebrother.

    – rsjethani

    11 de agosto de 2013 a las 14:15

  • ¿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 por extern en otro? deje que el enlazador resuelva el problema y oculte el resto del encabezado.

    – ste3e

    27 de enero de 2014 a las 5:32

avatar de usuario
Christian Gingras

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 extern funciones, por lo que voy a ignorar el uso de extern 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 podrían definirse en un código fuente diferente *.C o pueden estar ubicados en cualquier archivo binario (*.OBJ, *.LIB, *.DLL), por lo que es posible que no tengamos el código fuente.

Incluyamos de nuevo el encabezado. my_project.H en un diferente *.C archivo para entender mejor la diferencia. En el mismo proyecto, añadimos 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 static en un archivo de encabezado, el compilador/enlazador debe encontrar una instancia de una función con ese nombre en cada módulo que usa ese archivo de inclusión.

  • Una función que es parte de la biblioteca C puede ser reemplazada en un solo módulo redefiniendo un prototipo con static solo en ese modulo. Por ejemplo, reemplace cualquier llamada a malloc y free para agregar la función de detección de fugas de memoria.

  • el especificador extern no es realmente necesario para las funciones. Cuando static no se encuentra, siempre se supone que una función es extern.

  • 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 la inclusión de un archivo de encabezado de un único módulo.

    Muchos gerentes de proyecto 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 tales variables aparezcan solo sobre la primera función que las usa.

avatar de usuario
Steve Melnikoff

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; utilizando extern está bien, pero es redundante.

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

avatar de usuario
qris

Un muy buen articulo que me llego sobre el extern palabra clave, junto con los ejemplos: http://www.geeksforgeeks.org/understanding-extern-keyword-in-c/

Aunque no estoy de acuerdo en que usar extern en las declaraciones de funciones es redundante. Se supone que esto es una configuración del compilador. Así que recomiendo usar el extern en las declaraciones de funciones cuando sea necesario.

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

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

    –David Thornley

    30 de enero de 2009 a las 17:56

  • Parece que últimamente tengo este problema de leer mal las cosas. Lo lamento. Cuando era nuevo en C, #incluía “archivo.c” para incluir las funciones de un archivo directamente en el otro archivo. Luego descubrí cómo usar ‘extern’. Pensé que estaba cometiendo el mismo error que yo.

    – Chris Lutz

    30 de enero de 2009 a las 18:18

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad