La palabra clave extern en C es fundamental para trabajar con variables globales y funciones que están definidas en archivos diferentes al que estamos compilando. Cuando repartimos nuestro programa en varios archivos fuente, como main.c y funciones.c, el compilador necesita saber que ciertas funciones o variables existen en otros archivos para poder compilar correctamente cada unidad de compilación por separado.
En el caso de las funciones, normalmente basta con declarar sus prototipos en el archivo donde las usamos. Por ejemplo, si tenemos funciones como incrementar_contador e imprimir_contador definidas en funciones.c, en main.c solo necesitamos declarar sus prototipos:
void incrementar_contador(void);
void imprimir_contador(void);
Con esto, el compilador entiende que esas funciones existen en otra parte y deja que el enlazador se encargue de unirlas. En este contexto, usar extern explícitamente en los prototipos de funciones no es necesario, ya que el compilador asume implícitamente que son externas.
Sin embargo, cuando hablamos de variables globales, la situación cambia. Si tenemos una variable global, por ejemplo contador, definida en un archivo contador.c así:
unsigned int contador = 1;
y queremos usarla en otro archivo, como en funciones.c, debemos indicarle al compilador que esa variable existe en otro lugar. Para ello, usamos la palabra clave extern en la declaración:
extern unsigned int contador;
Esto le dice al compilador que contador no es una variable nueva en este archivo, sino que está definida externamente. Sin esta declaración, el compilador no sabe qué tipo tiene contador y puede generar errores o, en algunos compiladores, crear una variable global tentativa que puede causar conflictos al enlazar.
El comportamiento exacto puede variar según el compilador. Algunos, como Clang (parte de LLVM), permiten que si no usamos extern en la declaración de la variable global, se cree una variable tentativa que se resolverá en tiempo de enlace si existe una definición con valor inicial en otro archivo. Esto puede hacer que el programa compile y funcione aunque no hayamos puesto extern. Por ejemplo, si en funciones.c declaramos:
unsigned int contador;
sin inicializarla, y en contador.c la definimos con un valor inicial, el enlazador usará la definición con valor inicial y todo funcionará bien.
Pero otros compiladores, como ciertas versiones de GCC en Linux, no permiten esta ambigüedad y exigen que usemos extern para declarar variables globales definidas en otros archivos. Si no lo hacemos, el enlazador detectará múltiples definiciones de la misma variable y fallará.
Por lo tanto, para evitar problemas y asegurar portabilidad, es recomendable siempre declarar las variables globales externas con extern en los archivos donde las usamos, y definirlas sin extern en un único archivo donde se inicializan.
En resumen, extern es la forma que tiene C para indicarle al compilador que una variable global o función está definida en otro archivo, permitiendo que el compilador y enlazador gestionen correctamente las referencias cruzadas entre múltiples archivos fuente. Mientras que para funciones basta con sus prototipos, para variables globales es esencial usar extern para evitar conflictos y errores de compilación.