Programmation C et C++ - Utiliser le compilateur GCC


La collection de compilateurs GCC (GNU Compiler Collection), lancée en 1983 par Richard Stallman, constitue le composant de base du monde du logiciel libre. En effet, tous les logiciels libres résultent, directement ou indirectement, d'une compilation avec GCC étant donnés qu'ils ont été écrits en C ou avec un langage (Perl, Python...) lui même écrit en C.

En plus de la collection de compilateurs (gcc pour le langage C, g++ pour le langage C++,...), le système GCC, écrit en C et en assembleur, comprend également un éditeur de texte (Emacs), un Débogueur (GDB), un langage de script (Bash), et de nombreuses bibliothèques système comme glibc. Normalement GCC est nativement présent dans les distributions Ubuntu et notamment dans la distribution Ubuntu 18.04 utilisée ici.

1) Généralités

1.1) Vérifier la présence des compilateurs gcc et g++

On peut peut obtenir de l'information sur la version de GCC installée sur l'ordinateur à l'aide des commandes suivantes :
gcc -v, g++ -v, dpkg-query -l gcc, dpkg-query -l g++, type gcc, type g++, which gcc, which g++, gcc --help, g++ --help

ubuntu@ubuntu-MS-7C08:~$ gcc -v
COLLECT_GCC=gcc
gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)
ubuntu@ubuntu-MS-7C08:~$

La commande which permet d'identifier le répertoire d'installation de gcc et de g++
ubuntu@ubuntu-MS-7C08:~$ which gcc
/usr/bin/gcc
ubuntu@ubuntu-MS-7C08:~$ which g++
/usr/bin/g++

Si gcc n'est pas installé, on peut l'installer à l'aide des commandes suivantes :
sudo apt update
sudo apt install build-essential

En installant le paquet build-essential, on installe à la fois :
dpkg-dev - aquet d'outils de développement pour Debian;
g++ - compilateur GNU C++;
gcc - compilateur GNU C;
libc6-dev - lLibrairie GNU C de développement avec fichiers d'en-têtes
libc-dev - paquet virtuel fourni par libc6-dev
make - utilitaire pour réaliser des compilations

D'autre part, pour installer le manuel d'aide, relatif à la programmation sous Linux, on utilise la commande suivante :
sudo apt-get install manpages-dev

1.2) Identifier les commandes et les fichiers

La commande suivante compile le fichier source C essai.c et produit l'exécutable essai. Le paramètre -o (output file) indique au compilateur de donner le nom qui suit à l'exécutable.
gcc essai.c -o essai
Remarque: Avec la simple commande gcc essai.c, le compilateur nomme par défaut l'exécutable a.out.

La commande suivante compile le fichier source C++ essai.cpp et produit l'exécutable essai.
g++ essai.cpp -std=c++11 -Wall -Wextra -o fichier_executable

  • -std=c++11 : demande au compilateur d'utiliser le standard c++11 (c++11 et c++14 sont les dernières versions du standard C++)
  • -Wall : demande au compilateur de retourner les warnings sur des parties de codes incorrectes
  • -Wextra : cette option permet d'obtenir des messages de warning supplémentaires non activés avec la seule option -Wall
  • -o : permet de définir le nom de l'exécutable

La commande suivante compile le fichier source C++ essai.cpp et produit seulement un fichier objet (pas d'édition des liens donc pas d'exécutable)
g++ -c essai.cpp
-c : demande au compilateur de réaliser seulement une compilation

Dans tous les cas, pour exécuter le programme essai, on ouvre le terminal dans le répertoire du programme et on saisit la commande
./essai

Les type de fichiers utilisés ou prduits par gcc présentes les extensions suivantes:
.a librairie statique (archive);
.c code source C qui doit être prétraité (preprocessed);
.h code source C - fichier d'en-tête (header file);
.o fichier objet destiné à être fourni à l'éditeur des liens (linker);
.s code assembleur;
.so bibliothèque d'objets partagés (Shared object library) équivalente aux DLL utilisées sous Windows (Dynamic Link Library).

2) Exemples C

2.1) Projet composé d'un seul fichier source C

Le projet est ici constitué du seul fichier source essai.c

//------------- essai.c -----------------
#include <stdio.h>
int main (void)
{
printf ("Hello, world!\n");
return 0;
}

Pour créer directement le fichier exécutable essai (compilation et link), on saisit la commande suivante dans le terminal de commande qui doit être ouvert dans le répertoire où se trouve le fichier essai.c
gcc -Wall essai.c -o essai

L'option -Wall (warnings all) active tous les messages d'avertissement du compilateur. Il est conseillé de toujours utiliser cette option afin de s'assurer que le code source utilisé est parfait.

L'option -o permet de choisir le nom attribué à l'exécutable (essai dans le cas présent). En l'absence de cette option, la commande de compilation doit être gcc -Wall essai.c et dans ce cas, en l'absence de précision sur le nom de l'exécutable, le compilateurlui attribue par défaut le nom a.out.
Pour exécuter le fichier essai, on ouvre le Terminal dans le répertoire du programme et on saisit la commande ./essai (chemin relatif de l'exécutable). On peut aussi directement saisir le chemin complet du fichier, quel que soit l'endroit où est ouvert le Terminal commandes.
Par exemple /media/ubuntu/data/TEST/essai (chemin absolu de l'exécutable)

00

Remarque: L'option -S demande au compilateur de créer un fichier en assembleur
gcc -S essai.c

01

2.2) Projet composé de plusieurs fichiers source C

Fichiers sources: 2-2.zip

2.2.1) Compiler et linker des fichiers source en une fois

Dans le cas présent le projet est consitué de trois fichiers sources : mesfonctions.c, mesfonctions.h et essai.c.
Le fichier mesfonctions.c contient une fonction bonjour(nom) qui permet de prendre un nom en paramètre puis d'afficher "Bonjour !" suivi du nom passé en paramètre.

On crée directement le fichier exécutable essai à l'aide de la commande suivante:
gcc -Wall essai.c mesfonctions.c -o essai

02

2.2.2) Compiler et linker des fichiers sources de façon ciblée

Si le fichier mesfonctions.c n'est pas modifié, il est intéressant (pour des raisons de rapidité) d'éviter sa recompilation à chaque fois qu'on modifie le fichier essai.c.
Dans ce cas, on opère en deux étapes :
1) on crée d'abord les fichiers objets mesfonctions.o et essai.o puis
2) on linke ces fichiers pour créer l'exécutable essai
Ainsi quand on modifiera ultérieurement le fichier essai.c, on recompilera uniquement ce fichier puis on relancera l'édition des liens.

a) Compilation des fichiers mesfonctions.c et essai.c
La compilation des fichiers essai.c et mesfonctions.c produit les fichiers objet mesfonctions.o et essai.o.
Elle s'effectue à l'aide des commandes suivantes, avec le terminal de commande ouvert dans le répertoire où se trouvent les fichiers essai.c et mylib.c
gcc -Wall -c essaic
gcc -Wall -c mesfonctions.c
L'option -c conduit gcc à compiler uniquement (sans linker) ce qui produit les fichiers objets essai.o et mesfonctions.o sans fichier exécutable.

b) Edition des liens (link)
L'édition des liens des fichiers essai.o et /strong>mesfonctions.o produit le fichier excutable essai. Elle s'effectue à l'aide de la commande suivante, après avoir ouvert le terminal de commande dans le répertoire où se trouve les fichiers essai.o et mesfonctions.o
gcc essai.o mesfonctions.o -o essai

03

Remarque : Si le message d'erreur "error : undefined references !" apparaît lors de l'édition des liens, cela peut provenir du fait qu'il manque un fichier objet dans la liste.
Certains compilateurs affichent également ce message lorsque les fichiers objets sont indiqués dans un ordre qui ne convient pas. Normalement l'ordre "fichier objet appelant", "fichier objet appelé" doit convenir au compilateur.

2.3) Utiliser une librairie intégrée à GCC en C

Les librairies (ou bibliothèques) standard des langages C et C++ contiennent un ensemble de fonctions et de classes de base pour ces langages.
On distingue : Les librairies statiques (static library) et les librairies dynamiques (librairies d'objets partagés - Shared object library sous Linux ou DLL sous Windows)
Les librairies statiques sont intégrée dans l'exécutable de notre programme au moment de l'édition des liens.
Les librairies dynamiques ne sont pas intégrées à notre programme au moment de l'édition des liens mais sont chargées en mémoire et appelées de façon dynamique lors de l'exécution de notre programme

Pour les fichiers librairies, on rencontre les types d'extensions suivants :
.a = statique (GCC Linux)
.so = dynamique (GCC Linux)
.lib = statique (Microsoft Windows)
.dll = dynamique (Microsoft Windows)

Il est à noter que :

  • 1) Le compilateur MinGW ou Mingw32 (Minimalist GNU Windows) est une adaptation, à la plate-forme Windows, des logiciels de développement et de compilation GNU.
  • 2) Les librairies compatibles avec MinGW ne sont cependant pas toujours compatibles avec les librairies utilisées par Visual Studio (Outil de développement intégé de Microsoft)
  • 3) Sous Linux, une librairie statique est créée à partir de fichiers objets, à l'aide de la commande ar (outil GNU archiver ar)
  • 4) Les librairies standards sont prises automatiquement en compte par gcc mais pour les autres librairies il faut préciser leur chemin au moment de l'édition des liens (link)

Etant donné que dans l'exemple suivant on utilise une librairie mathématique standard qui est intégrée (et dont chemin est connu) à gcc. La commande de compilation et d'édition des liens est simplement
gcc -Wall essai.c -o essai

Dans cet exemple, on a pas besoin de préciser le nom et le chemin de la librairie mathématique utilisée car la fonctions mathématique appelée est implémentée par les fonctions intégrées au compilateur (built-in function)

04

2.4) Créer et utiliser une librairie statique en C

Fichiers sources: 2-4.zip

Les possibilités offertes par les langages C et C++ peuvent être étendues à l'aide de librairies additionnelles diverses et variées créées par les développeurs.
Pour créer une librairie statique, utilisable en C, on opère de la façon suivante.
Soient deux fichiers sources fonction1.c et fonction2.c destinés à faire partie de la bibliothèque statique (static library) qu'on souhaite créer.

On commence par transformer ces deux fichiers en fichiers objets.
Ensuite, on crée la librairie statique malib.a intégrant les deux fichiers objets précédents. Pour cela, on utilise l'outil ar avec l'option -r.
Cette commande crée la librairie si celle-ci n'existe pas ou la met à jour si elle existe déjà.
Puis on compile et on fait l'édition des liens, de notre programme essai.c avec la librairie malib.a.
Enfin on exécute directement le programme avec la commande ./essai

gcc -c fonction1.c fonction2.c
ar -r malib.a fonction1.o fonction2.o
gcc essai.c malib.a -o essai
./essai

05

2.5) Créer et utiliser une librairie partagée en C

Fichiers sources (idem2.4): 2-4.zip

Pour créer une librairie d'objets partagés, utilisable en C, on opère de la façon suivante. Soient les deux fichiers sources fonction1.c et fonction2.c précédents destinés à faire partie de la bibliothèque partagée (shared library) qu'on souhaite créer.

On commence par créer les deux fichiers objets correspondant aux deux fichiers sources. L'option -c indique au compilateur de créer des fichiers objets. L'option -fpic (position independent code) demande au compilateur de créer des modules objets pouvant être adressés de façon dynamique.
Ensuite on crée la librairie d'objets partagés malib.so.
Puis on compile notre programme essai.c en le linkant avec la librairie d'objets partagés.
Ensuite, pour pouvoir exécuter le programme essai,on commence par indiquer à GCC le chemin du répertoire où se trouve la librairie dynamique, à l'aide de la commande export LD_LIBRARY_PATH=/chemin_librairie

gcc -c -fpic fonction1.c fonction2.c
gcc -shared fonction1.o fonction2.o -o malib.so
gcc essai.c malib.so -o essai
export LD_LIBRARY_PATH=/media/ubuntu/data/TEST
./essai

06

On peut également combiner les commandes 1 et 2 précédentes en une seul commande :
gcc -fpic -shared fonction1.c fonction2.c -o malib.so

On peut également, à la place des commandes 3 et 4 précédente, créer l'exécutable en lui intégrant le chemin de la librairie partagée
gcc -Wl,-R/media/ubuntu/data/TEST -L/media/ubuntu/data/TEST -o essai essai.c malib.so
./essai

07

Remarques:
LD_LIBRARY_PATH est une variable d'environnement de Linux dans laquelle on peut indiquer le nom des chemins (séparées par :) des bibliothèques partagées qui vont être utilisées par un programme qu'on va exécuter. Les librairies partagées nécessaires sont tout d'abord recherchées, par un programme en cours d'exécution, dans la liste des chemins indiqués dans LD_LIBRARY_PATH. Elles sont ensuite recherchées par ce progamme dans les chemins standard (/lib, /usr/lib...) si elles n'ont pas été trouvées dans la liste des chemins indiqués dans LD_LIBRARY_PATH. Il est conseillé de définir le contenu de LD_LIBRARY_PATH en ligne de commande ou dans un script, juste avant d'exécuter le programme. Ainsi, le nouveau LD_LIBRARY_PATH reste isolé du reste du système (il n'est plus actif dès que le terminal est refermé).
Sous Windows, la variable d'environnement utilisée par Windows pour rechercher les DLL est PATH (et non pas LD_LIBRARY_PATH)

A la différence de LD_LIBRARY_PATH, la variable d'environnement LIBRARY_PATH est utilisé par gcc, non pas après mais lors de la compilation, pour prendre en compte les répertoires contenant des bibliothèques statiques et partagées qui doivent être liées à un programme.
Dans ce cas, pour compiler le programme, on doit utiliser les options -R, -L et éventuellement -I (pour les fichiers include). En procédant ainsi, un programme essai utilisant une librairie partagée peut être ensuite directement exécuté autant de fois que l'on veut mais la librairie utilisée doit obligatoirement se trouver à l'endroit indiqué car le nom du chemin correspondant est intégré au programme.
gcc -Wl,-R/path/to/lib -I/path/to/include -L/path/to/lib -o essai essai.c malib.so

Exemples
gcc -Wl,-R/media/ubuntu/data/TEST -I/media/ubuntu/data/TEST -L/media/ubuntu/data/TEST -o essai essai.c malib.so
./essai

gcc -Wl,-R/media/ubuntu/data/TEST -L/media/ubuntu/data/TEST -o essai essai.c malib.so
./essai

Dans une commande de compilation, le nom des librairies doit apparaître après celui des fichiers sources utilisateurs (et non pas avant), en suivant l'ordre des d'appels de fonctions. Il ne faut pas oublier d'inclure dans les programmes source le fichier d'entête .h qui correspond à la librairie utilisée.

3) Exemples C++

3.1) Projet composé d'un seul fichier source C++

Fichiers sources : 3-1.zip

Le projet est ici constitué du seul fichier source C++ essai.cpp

Pour créer directement le fichier exécutable essai (compilation et link), on saisit la commande suivante dans le terminal de commande qui doit être ouvert dans le répertoire où se trouve le fichier essai.cpp
g++ -Wall essai.cpp -o essai

10

3.2) Projet composé de plusieurs fichiers source C++

Fichiers sources: 3-2.zip

3.2.1) Compiler et linker des fichiers source en une fois en C++

Dans le cas présent le projet est consitué de trois fichiers sources : mesclasses.cpp, mesclasses.h et essai.cpp. Le fichier mesclasses.cpp contient une classe Affiche.

On crée directement le fichier exécutable essai (option -o) puis on l'exécute à l'aide des commande suivantes:
g++ -Wall essai.cpp mesclasses.cpp -o essai
./essai

11

3.2.2) Compiler et linker des fichiers sources de façon ciblée en C++

On reprend les fichier sources précédents.
1) on crée d'abord les fichiers objets mesclasses.o et essai.o (option -c) puis
2) on linke ces fichiers pour créer l'exécutable essai
Ainsi quand on modifiera ultérieurement le fichier essai.cpp, on recompilera uniquement ce fichier puis on relancera l'édition des liens.

1) Compilation de mesclasses.cpp et essai.cpp
La compilation de essai.cpp et maclasse.cp produit les fichiers objet maclasse.o et essai.o. Elle s'effectue à l'aide des commandes suivantes, avec le terminal de commande ouvert dans le répertoire où se trouvent les fichiers essai.cpp et mesclasses.cpp

g++ -Wall -c essai.cpp
g++ -Wall -c mesclasses.cpp
g++ -Wall essai.o mesclasses.o -o essai

L'option de compilation -c conduit g++ à compiler uniquement (sans linker) ce qui produit les fichiers objets essai.o et maclasse.o sans fichier exécutable. L'édition des liens deessai.o et maclasse.o produit le fichier excutable essai.

12

3.3) Créer et utiliser une librairie statique en C++

Fichiers sources: 3-3.zip

Pour créer une librairie statique, utilisable en C++, on opère de la façon suivante. Soient les fichiers sources voiture.h, voiture.cpp et bonjour.cpp destinés à faire partie de la bibliothèque statique (static library) C++ qu'on souhaite créer.

On compile ces fichiers pour constituer les fichiers objets puis on utilise l'outil ar pour créer la librairie statique qu'on appelle malib.a (par exemple).
Puis on utilise le fichier source essai.cpp pour créer l'éxécutable essai qui utilise la librairie malib.a

g++ -c bonjour.cpp
g++ -c voiture.cpp
ar -r malib.a bonjour.o voiture.o
g++ essai.cpp malib.a -o essai

13

3.4) Créer et utiliser une librairie dynamique en C++

Fichiers sources (idem 3-3): 3-3.zip

Pour créer une librairie d'objets partagés, utilisable en C++, on opère de la façon suivante. Soient les fichiers sources précédents voiture.h, voiture.cpp et bonjour.cpp, destinés à faire partie de la bibliothèque d'objets partagés (shared library) C++ qu'on souhaite créer.

Comme précédemment, on commence par créer les deux fichiers objets correspondant aux deux fichiers sources. L'option -c indique au compilateur de créer des fichiers objets. L'option -fpic (position independent code) demande au compilateur de créer des modules objets pouvant être adressés de façon dynamique. Puis on crée la librairie d'objets partagés. Puis on crée l'exécutable en le reliant à la librairie partagée. Puis on l'exécute après avoir précisé le chemin de la librairie partagée.

g++ -c -fpic voiture.cpp bonjour.cpp
g++ -shared voiture.o bonjour.o -o malib.so
g++ essai.cpp malib.so -o essai
export LD_LIBRARY_PATH="/media/ubuntu/data/TEST"
./essai

14

On peut également créer un exécutable qui prend en compte l'emplacement de la librairie utilisée, à l'aide de la commande suivante, mais dans ce cas la librairie doit obligatoirement se trouver à l'endroit indiqué au moment de l'exécution car le nom du chemin correspondant est intégré au programme.
g++ -Wl,-R/media/ubuntu/data/TEST -L/media/ubuntu/data/TEST essai.cpp malib.so -o essai

15