Existem dois conceitos distintos em jogo aqui. Declaração e definição.
Vamos criar 4 arquivos diferentes.
// cabecalho.h
#ifndef CABECALHO_H_
#define CABECALHO_H_
extern int var;
int getVar();
#endif
Neste arquivo declaramos os identificadores vare getVar, os quais, nesse momento, não existem em memória, mas funcionam unicamente para informar ao compilador que ele deve manter estes símbolos na tabela e seus tipos são como supra-mencionados.
// arquivo1.c
#include "cabecalho.h"
int var = 10;
Neste arquivo definimos o identificador var e inicializamos seu valor para 10. A partir deste momento o mesmo possui endereço em memória.
// arquivo2.c
#include "cabecalho.h"
int getVar() {
return ++var;
}
Neste arquivo definimos o identificador getVar.
// main.c
#include <stdio.h>
#include "cabecalho.h"
int main() {
for (int i = 0; i < 5; i++)
printf("%d\n", getVar());
return 0;
}
Teste a função main e você verá que o valor de var é alterado consistentemente.