Alors quand tu as 2 fichiers .c
Tu dis au compilateur de compiler fichier1.c (qui peut inclure fichier1.h et fichier2.h).
gcc -c fichier1.c
A ce moment, le compilateur transforme tout ce qu'il voit en langage assembleur. Mais si dans fichier1 il y a un appel à une fonction de fichier2, le compilateur à ce moment ne trouve pas le code source de la fonction puisqu'elle est dans le fichier2.
La technique des signatures de fonctions est appliqué: le compilateur se dit qu'il n'a pas trouvé la fonction mais qu'elle se situera peut-être quelquepart. Alors, en attendant, il crée une signature de la fonction de fichier2 (une étiquette comportant entre autres le nom de la fonction).
Il crée généralement cette signature grace au prototype de fonction qui a été défini dans le fichier2.h
ensuite il continue la compilation de fichier1. Quand il termine il génère un fichier objet fichier1.o (ou .obj selon les compilateurs) contenant le code en langage machine (ou assembleur c'est pareil) de fichier1.c
Ensuite, tu compiles ton fichier2.c
gcc -c fichier2.c
De la même manière (avec les signatures du code des fonctions inconnues) il génère un fichier2.obj
L'editeur de lien (ce que tu appelle lieur ou linker en anglais) permet de prendre le code des 2 fichiers objets et d'en faire un gros fichier exécutable.
gcc fichier1.o fichier2.o -o mon_programme
Là, il prend tous les fichiers objet et résoud les signatures en les remplaçant par des liens physiques sur l'autre partie du code.
On peut voir ça comme si fichier1 était un truc avec des prises mâles et fichiers2 un truc avec des prises femelles, l'éditeur connecte chaque prise et met le tout dans un fichier exécutable.