đ Retour au Sommaire
Quand vous Ă©crivez un programme en C et que vous le « compilez », vous pensez peut-ĂȘtre quâil sâagit dâune seule opĂ©ration magique qui transforme votre code en programme exĂ©cutable. En rĂ©alitĂ©, cette transformation passe par 4 Ă©tapes distinctes, chacune ayant un rĂŽle bien prĂ©cis.
Comprendre ces étapes vous aidera à :
Voici le parcours complet de votre code source jusquâĂ lâexĂ©cutable :
Code source (.c)
â
âŒ
ââââââââââââââââââââ
â 1. PRĂPROCESSEUR â âââș Fichier prĂ©traitĂ© (.i)
ââââââââââââââââââââ
â
âŒ
ââââââââââââââââââââ
â 2. COMPILATION â âââș Code assembleur (.s)
ââââââââââââââââââââ
â
âŒ
ââââââââââââââââââââ
â 3. ASSEMBLAGE â âââș Fichier objet (.o)
ââââââââââââââââââââ
â
âŒ
ââââââââââââââââââââ
â 4. ĂDITION LIENS â âââș ExĂ©cutable (a.out ou nom choisi)
ââââââââââââââââââââ
Prenons un exemple concret avec ce programme simple que nous appellerons hello.c :
#include <stdio.h>
#define MESSAGE "Bonjour le monde!"
int main(void) {
printf("%s\n", MESSAGE);
return 0;
}
Voyons maintenant ce qui se passe à chaque étape.
Le prĂ©processeur est un programme qui traite votre code source avant la compilation proprement dite. Il sâoccupe de toutes les lignes qui commencent par # (les directives de prĂ©processeur).
Inclusion de fichiers (#include) : Il remplace chaque directive #include par le contenu complet du fichier référencé. Par exemple, #include <stdio.h> est remplacé par des milliers de lignes contenant les déclarations de fonctions comme printf, scanf, etc.
Expansion des macros (#define) : Il remplace chaque utilisation dâune macro par sa valeur. Dans notre exemple, MESSAGE sera remplacĂ© par "Bonjour le monde!" partout oĂč il apparaĂźt.
Compilation conditionnelle (#ifdef, #ifndef, #endif) : Il inclut ou exclut des portions de code selon certaines conditions.
Suppression des commentaires : Tous vos commentaires (// et /* */) sont retirés.
Avec GCC, vous pouvez arrĂȘter le processus aprĂšs le prĂ©processeur en utilisant lâoption -E :
gcc -E hello.c -o hello.i
Le fichier hello.i généré est un fichier texte que vous pouvez ouvrir. Vous y verrez :
stdio.h (plusieurs milliers de lignes)MESSAGE remplacĂ© par "Bonjour le monde!"Le fichier de sortie porte gĂ©nĂ©ralement lâextension .i (pour intermediate ou preprocessed).
fichier.h: No such file or directory â Le fichier dâen-tĂȘte nâest pas trouvĂ©Câest lâĂ©tape oĂč le code C prĂ©traitĂ© est transformĂ© en code assembleur, un langage de trĂšs bas niveau spĂ©cifique Ă lâarchitecture de votre processeur (x86, ARM, etc.).
Analyse lexicale : Le compilateur découpe votre code en « tokens » (mots-clés, identifiants, opérateurs, etc.)
Analyse syntaxique : Il vĂ©rifie que votre code respecte la grammaire du langage C. Câest ici que sont dĂ©tectĂ©es les erreurs comme un point-virgule oubliĂ© ou une parenthĂšse manquante.
Analyse sĂ©mantique : Il vĂ©rifie le sens de votre code. Par exemple, il sâassure que vous nâadditionnez pas un entier avec une chaĂźne de caractĂšres.
Génération de code : Il produit le code assembleur équivalent à votre programme C.
Optimisation : Selon les options choisies, le compilateur peut optimiser le code pour le rendre plus rapide ou plus compact.
Utilisez lâoption -S pour obtenir le code assembleur :
gcc -S hello.c -o hello.s
Le fichier hello.s contient du code assembleur. Voici un extrait simplifié de ce que vous pourriez voir :
.file "hello.c"
.section .rodata
.LC0:
.string "Bonjour le monde!"
.text
.globl main
main:
pushq %rbp
movq %rsp, %rbp
leaq .LC0(%rip), %rdi
call puts@PLT
movl $0, %eax
popq %rbp
ret
Ne vous inquiĂ©tez pas si ce code vous semble incomprĂ©hensible ! Lâimportant est de comprendre que câest une reprĂ©sentation textuelle des instructions que votre processeur exĂ©cutera.
Le fichier de sortie porte lâextension .s (pour assembly source).
expected ';' before... â Point-virgule oubliĂ©undeclared identifier â Variable non dĂ©clarĂ©eincompatible types â Types incompatiblesimplicit declaration of function â Fonction utilisĂ©e sans dĂ©clarationLâassembleur transforme le code assembleur (texte lisible par un humain) en code machine (instructions binaires comprĂ©hensibles par le processeur). Le rĂ©sultat est appelĂ© fichier objet.
Traduction : Chaque instruction assembleur est convertie en son équivalent binaire (code machine).
CrĂ©ation de la table des symboles : Lâassembleur crĂ©e une liste de tous les symboles (noms de fonctions, variables globales) dĂ©finis ou utilisĂ©s dans le fichier.
GĂ©nĂ©ration des informations de relocation : Il note les endroits oĂč des adresses devront ĂȘtre ajustĂ©es lors de lâĂ©dition de liens.
Utilisez lâoption -c pour produire uniquement le fichier objet :
gcc -c hello.c -o hello.o
Le fichier hello.o est un fichier binaire. Vous ne pouvez pas le lire directement avec un éditeur de texte, mais vous pouvez examiner son contenu avec des outils comme objdump :
objdump -d hello.o # Affiche le désassemblage
objdump -t hello.o # Affiche la table des symboles
Le fichier de sortie porte lâextension .o sous Linux/macOS (pour object) ou .obj sous Windows.
Un fichier objet nâest pas encore exĂ©cutable. Il contient le code machine de votre fichier source, mais :
printf) ne sont pas encore connues.c, chacun produit son propre .oCâest lâĂ©tape suivante qui assemblera le tout.
Les erreurs Ă cette Ă©tape sont rares car la plupart des problĂšmes sont dĂ©tectĂ©s lors de la compilation. Vous pourriez rencontrer des erreurs si lâassembleur de votre systĂšme a des limitations spĂ©cifiques.
LâĂ©diteur de liens (ou linker) est le programme qui assemble tous les fichiers objets et les bibliothĂšques nĂ©cessaires pour crĂ©er lâexĂ©cutable final.
RĂ©solution des symboles : Il connecte les appels de fonctions Ă leurs dĂ©finitions. Par exemple, quand votre code appelle printf, le linker trouve oĂč se trouve rĂ©ellement cette fonction (dans la bibliothĂšque C standard).
Fusion des sections : Il combine les différentes sections (code, données, etc.) de tous les fichiers objets.
Relocation : Il ajuste toutes les adresses pour quâelles correspondent Ă leur position finale dans lâexĂ©cutable.
Liaison avec les bibliothÚques : Il intÚgre le code des bibliothÚques utilisées (statiquement) ou note les dépendances (dynamiquement).
Câest lâĂ©tape finale, celle qui se produit quand vous compilez normalement :
gcc hello.o -o hello
Ou directement depuis le source :
gcc hello.c -o hello
LâexĂ©cutable hello est maintenant prĂȘt Ă ĂȘtre lancĂ© :
./hello
# Affiche : Bonjour le monde!
Il existe deux types de liaison avec les bibliothĂšques :
Liaison statique : Le code de la bibliothÚque est copié dans votre exécutable. Celui-ci devient plus gros mais autonome.
Liaison dynamique : Votre exĂ©cutable contient juste une rĂ©fĂ©rence Ă la bibliothĂšque. Elle sera chargĂ©e en mĂ©moire au moment de lâexĂ©cution. Câest le comportement par dĂ©faut sous Linux.
undefined reference to 'fonction' â Une fonction est appelĂ©e mais jamais dĂ©finie. Soit vous avez oubliĂ© dâinclure un fichier .o, soit il manque une bibliothĂšque.multiple definition of 'symbole' â Un symbole (fonction ou variable) est dĂ©fini plusieurs fois.| Ătape | EntrĂ©e | Sortie | Option GCC | RĂŽle principal |
|---|---|---|---|---|
| 1. Préprocesseur | .c |
.i |
-E |
Traite les directives # |
| 2. Compilation | .i |
.s |
-S |
Traduit en assembleur |
| 3. Assemblage | .s |
.o |
-c |
Traduit en code machine |
| 4. Ădition de liens | .o |
exĂ©cutable | (dĂ©faut) | CrĂ©e lâexĂ©cutable final |
# Ătape 1 : PrĂ©processeur uniquement
gcc -E hello.c -o hello.i
# Ătape 2 : Jusqu'Ă la compilation (assembleur)
gcc -S hello.c -o hello.s
# Ătape 3 : Jusqu'Ă l'assemblage (fichier objet)
gcc -c hello.c -o hello.o
# Ătape 4 : Processus complet (exĂ©cutable)
gcc hello.c -o hello
Quand une erreur survient, le message vous indique souvent Ă quelle Ă©tape elle sâest produite :
#include ou #defineDans un vrai projet, vous avez généralement plusieurs fichiers .c. La compilation séparée permet de ne recompiler que les fichiers modifiés :
gcc -c fichier1.c -o fichier1.o
gcc -c fichier2.c -o fichier2.o
gcc -c fichier3.c -o fichier3.o
gcc fichier1.o fichier2.o fichier3.o -o mon_programme
Si vous modifiez uniquement fichier2.c, vous nâavez quâĂ refaire :
gcc -c fichier2.c -o fichier2.o
gcc fichier1.o fichier2.o fichier3.o -o mon_programme
Câest exactement ce quâautomatisent les outils comme Make et CMake que vous dĂ©couvrirez plus tard dans cette formation.
La chaĂźne de compilation en C nâest pas une boĂźte noire mystĂ©rieuse. Elle se compose de quatre Ă©tapes bien distinctes, chacune avec son rĂŽle :
#Comprendre ce processus est fondamental pour tout dĂ©veloppeur C. Vous serez maintenant mieux armĂ© pour comprendre les messages dâerreur et optimiser vos compilations !
âïž Visualisation du pipeline