Générer un programme

Le processus

La génération d’un programme (build en anglais) désigne le processus consistant à transformer des codes sources au format texte en programme exécutable sur un type de machines donné. Ce processus repose sur deux actions :

  • La compilation qui transforme 1 fichier source (.cpp) en 1 fichier objet avec l’extension .obj sous Windows et .o sous Linux. Un fichier objet correspond grosso-modo à un morceau du programme final. Un utilitaire pouvant accomplir cette tâche est appelé compilateur.

  • La liaison (ou link en anglais) qui fusionne les différents fichiers objets pour obtenir le programme exécutable final. Un utilitaire pouvant effectuer cette tâche s’appelle un éditeur de liens (linker en anglais).

Le compilateur et l’éditeur de liens peuvent être des programmes séparés ou un même programme, cela n’a pas d’importance pour le processus.

Voici un schéma qui représente le traitement des différents fichiers sources et des fichiers objets durant l’étape de Build :

../_images/_compil.png

Note

Les fichiers sources sont compilés indépendamment. Cela signifie que lorsqu’un fichier source est compilé, il n’a pas connaissance du contenu des autres fichiers sources ! Cela peut vous paraître étrange, mais il y a un avantage à cela. En effet, lorsqu’on modifie un fichier source et que l’on relance la séquence de Build, seul ce fichier source est recompilé, les autres fichiers objets sont conservés tels quels. L’étape de liaison doit, dans tous les cas, reprendre la totalité de ses traitements dès qu’un fichier source est modifié.

Pour mettre en place l’étape de Build, plusieurs approches sont possibles :
  • Le terminal dans lequel vous lancez manuellement ces opérations.

  • L’utilisation d’un utilitaire (comme Make ou CMake) automatisant le process de build en utilisant des fichiers texte pour décrire les étapes à effectuer.

  • L’utilisation d’un environnement de développement vous permettant par un clic sur l’icône de Build de générer le projet complet.

Avertissement

Dans le langage oral, on remplace souvent l’expression « générer un programme » par l’expression « compiler un programme ». Cela vient du fait que lors d’un build, l’étape de compilation prend le plus de temps, l’étape de liaison étant rapidement effectuée à la fin du processus.

L’étape de compilation produit des erreurs de compilation ! Ces dernières concernent essentiellement la syntaxe du programme. Chaque erreur de syntaxe est localisée dans un fichier source à une position précise. L’étape de liaison produit des erreurs de liaison ! Par exemple, une erreur de liaison se produit lorsqu’une fonction est appelée dans le programme alors qu’aucun code associé à cette fonction n’a été trouvé dans l’ensemble des fichiers sources.

Indiquez si les affirmations suivantes sont vraies ou fausses :

  1. L’étape de compilation traduit un fichier objet en fichier exécutable.

  2. Si vous avez 3 fichiers .cpp et un fichier .exe, vous aurez 4 fichiers objets.

  3. L’étape de liaison a lieu avant l’étape de compilation.

  4. Un fichier source est recompilé lorsqu’il est mis à jour.

  5. Un build représente la traduction du code source en programme exécutable.

La préparation du code

Au début de l’étape de compilation, une série de transformations sont appliquées au code source afin de le standardiser. Nous présentons certaines de ces étapes :

  • Retrait des commentaires. Les commentaires présents dans le code sont retirés :

int a=4;      // commentaire

=> int a=4;
int a = 4;
/* superbe
  documentation
   blablabla    */
int b = 5;

=> int a = 4;
=> int b = 5;
  • Preprocessing. Il est à ce niveau possible d’effectuer certaines transformations de manière automatique sur le code source grâce à un programme appelé préprocesseur (preprocessor). Les directives du préprocesseur sont données par les lignes commençant par le signe #. Nous étudierons plus tard son rôle. Voici un exemple du résultat obtenu pour la compilation d’un code source sous Windows :

#ifdef __linux__                         =>
    string plateforme = "LINUX";         =>
#elif _WIN32                             =>
    string plateforme = "WINDOWS";       =>          string plateforme = "WINDOWS";
#endif                                   =>
  • Fusion des lignes. Pour répartir une ligne de code sur plusieurs lignes, il est possible d’utiliser le caractère oblique \ suivi d’un retour à la ligne. Cette astuce permet notamment de répartir une chaîne de caractères sur plusieurs lignes, ce qui donne par exemple :

string exemple = ""\
"Cet exemple est sensé représenter " \
"la découpe d'une phrase trop longue"\
" pour apparaître en totalité à l'écran";

=>  string exemple = "Cet exemple est sensé représenter la découpe d'une phrase trop longue pour apparaître en totalité à l'écran";
  • Fusion de chaînes. Des chaînes de caractères côte à côte sont automatiquement fusionnées :

string exemple = "TOTO" "TITI";

=>  string exemple = "TOTOTITI";

Indiquez si les affirmations suivantes sont vraies ou fausses :

  1. Les étapes de préparation mettent en forme le code source en début de compilation.

  2. Les commentaires perdurent après l’étape de préparation.

  3. Le préprocesseur effectue des opérations automatiques dans le code source.

  4. Le signe \ sert uniquement à la division entre deux nombres.

Les tokens du C++

Durant l’étape de compilation, après qu’une série de transformations aient été appliquées pour transformer le code source en code standardisé, le compilateur va alors commencer à analyser le contenu du code pour qualifier le rôle de chaque élément. Ce traitement ressemble à l’analyse grammaticale que l’on effectue dans notre propre langage, on recherche : le sujet, le verbe et les compléments à l’intérieur d’une phrase. Ainsi se définit un token (anglicisme), il s’agit de la plus petite unité lexicale d’un programme du point de vue du compilateur.

Chaque élément existant dans la grammaire du C++ porte un nom particulier. Connaître cette terminologie, c’est un moyen de mieux comprendre la structuration de ce langage. Pourquoi s’intéresser à la terminologie d’un langage aussi complexe que le C++ ? Lorsque vous allez chercher de l’information en ligne sur des sites comme stackoverflow ou sur des blogs spécialisés, vous remarquerez que des mots particuliers reviennent de manière récurrente lorsque l’on parle du C++. Ignorer cette terminologie, c’est se priver de nombreuses sources d’information et d’assistance. De plus, la terminologie du C++ se retrouve dans beaucoup d’autres langages, la connaître, c’est aussi l’occasion d’identifier des similarités.

Nous précisons ci-dessous les principaux éléments de la structure du C++ :

  • Les noms (identifiants)

  • Les mots-clefs

  • Les littéraux

  • Les signes

Les noms

Le C++ permet au programmeur de nommer librement les variables, les tableaux, les fonctions, les structures et les classes. Ces noms sont appelés noms (name) ou plus précisément des identifiants (identifiers). Les identifiants qu’ils soient associés à un nom de variable, de fonction ou de classe, doivent tous respecter les mêmes règles :

  • Le premier caractère d’un identifiant doit commencer par une lettre de l’alphabet ou par un trait _ de soulignement. En conséquence, aucun identifiant ne peut commencer par un chiffre.

  • Pas de caractères spéciaux ou d’espaces à l’intérieur d’un identifiant.

  • Pas de mots-clés.

Note

En C++, les caractères majuscules et minuscules ont des significations différentes. On dit que le langage est sensible à la casse. Ainsi, Total et total sont considérés comme deux identifiants différents, associés par exemple à deux variables distinctes.

Note

Un nom permet de désigner une entité dans un programme C++. On parle de nom de variable, de nom de fonction ou de nom de classe… La notion de nom et celle d’identifiant sont quasi identiques. Il existe cependant de très rares cas où un nom se construit sans identifiant en utilisant des mots-clefs et des symboles du langage. On peut par exemple citer la fonction operator + désignant le nom de la fonction d’addition associé au symbole +.

Les mots-clefs

Un mot-clef (keyword) appelé aussi un mot réservé, représente un mot appartenant à la liste des mots réservés du langage. La liste des mots-clefs est établie par la norme du langage qui évolue en fonction des versions du C++. Il est interdit d’utiliser ces mots-clefs pour un nom de variable, de fonction ou de classe. Dans le tableau ci-dessous, nous vous présentons les plus classiques qui vous seront sûrement déjà familiers.

Exemple de mots-clefs du C++

bool

char

class

do

double

else

false

float

for

if

int

return

unsigned

this

true

void

while

Les mots-clefs tiennent différents rôles :
  • true et false : correspondent à des constantes de type booléen.

  • bool, char, float, double, int, void : sont associés aux types du C++.

  • if, for, while : représentent des instructions du langage servant à gérer le flux de contrôle d’un programme.

Les littéraux

Un littéral (literal) désigne une valeur écrite dans le code source du programme. On trouve aussi le terme de constante (constant) utilisé de manière similaire dans la communauté. Ils sont divisés en sous-catégories :

Les signes

Il existe une liste de signes en C++ appelés Ponctuators (anglicisme) : ! % ^ & * ( ) - + = { } | ~ [ ] ; “ :  » < > ? , . /

Chacun de ces signes, suivant le contexte où il est utilisé, peut prendre des significations différentes. Prenons le cas de la paire de parenthèses :

  • Dans l’écriture : a = fnt(), elles représentent un appel de fonction.

  • Dans l’écriture : 4/(5-a), elles indiquent l’ordre dans lequel un calcul sera effectué.

  • Dans l’écriture : void fnt(int a), elles permettent de lister les paramètres d’une fonction.

Vous pouvez retrouver pour chaque signe sa liste d’interprétations possibles.

Le compilateur lancera ensuite une analyse grammaticale pour déterminer suivant le contexte le rôle de chaque signe. Dans les rôles les plus fréquents, on trouve principalement :

  • Délimiteur : il sert à indiquer la fin d’une instruction comme les accolades { } ou le ;

  • Opérateur : il effectue un traitement en vue de retourner une valeur comme les opérateurs arithmétiques : + - /

Indiquez si les affirmations suivantes sont vraies ou fausses :

  1. En C++, on commence un commentaire par //, on peut donc affirmer que // est un opérateur de commentaire.

  2. 8.235 est un littéral.

  3. for, while sont des mots-clefs du C++.

  4. On peut choisir int comme nom de variable.

  5. Le C++ est un langage non sensible à la casse.

  6. Une paire de parenthèses correspond toujours à un appel de fonction.

  7. Le développeur peut définir de nouveaux mots-clefs.

  8. Les accolades sont des signes du langage C++.