.cpp => .exe (compilation)

Le
thitou
Bonjour,

puisque vous m'avez trés bien répondu hier, je me permets de vous
poser une autre question.

C'est au sujet de la compilation d'un projet C++, que se passe t-il au
juste entre mon code et l'executable ?

Pre-proceseur, compilateur, éditions de liens, j'en ai entendu parler,
mais concrétement je ne sais pas ce que ca fait.

Merci pour vos réponses.

Thierry

  • Partager ce contenu :
Vos réponses
Trier par : date / pertinence
Fabien LE LEZ
Le #797589
On 5 Aug 2004 10:52:29 -0700, (thitou):

C'est au sujet de la compilation d'un projet C++, que se passe t-il au
juste entre mon code et l'executable ?

Pre-proceseur, compilateur, éditions de liens


1ère étape : le préprocesseur.
C'est (en gros) un simple remplaceur de texte.
Il remplace
#include "machin.h"
par le contenu de "machin.h"
Si tu as écrit
#define Machin Truc
il enlève la ligne en question et remplacera toutes les occurences de
"Machin" par "Truc".

Si tu as écrit
#if (condition)
[code]
#endif

si (condition) est fausse, le préprocesseur enlève carrément le code,
de façon à ce que le compilo ne puisse pas le voir.

[Attention, la suite est encore plus schématique !]

Ensuite, le compilateur prend le résultat du préprocesseur et le
transforme en langage machine.

Enfin, l'éditeur de lien cherche la fonction main(). Quand il l'a
trouvée, il indique son adresse comme point de départ de l'exécution
du .exe. Puis, il cherche chaque fonction appelée par main(), et
repère son adresse pour qu'elle puisse être appelée. Et ainsi de
suite, récursivement.
Il peut en profiter pour repérer quelles fonctions ne seront pas
utilisées, et ainsi réduire la taille du .exe en supprimant le code
mort.

J'ai mis un exemple sur un code en C, le même code après son passage par le préprocesseur, et
enfin le code en assembleur (l'assembleur étant le langage lisible par
un humain, le plus proche possible du langage machine).
Note : au départ, j'ai tenté de faire un programme en C++, mais les
entêtes (<vector> par exemple) sont tellement gros que je me
retrouvais avec un fichier préprocessé de 5 Mo...).


--
;-)

thitou
Le #797355
merci fabien pour ces infos.

Et comment est-ce que ca se passe avec des librairies compilées avec le projet ?

Si je comprend bien :

1- Preprocesseur (remplacement de texte)
2- Compilation des .cpp en .o
3- Edition de lien (avec les librairies ?)
4- L'exécutable est généré.

Quel est le travail de make sous Linux ?

thierry
thitou
Le #797354
merci fabien pour ces infos.

Et comment est-ce que ca se passe avec des librairies compilées avec le projet ?

Si je comprend bien :

1- Preprocesseur (remplacement de texte)
2- Compilation des .cpp en .o
3- Edition de lien (avec les librairies ?)
4- L'exécutable est généré.

Quel est le travail de make sous Linux ?

thierry
Fabien LE LEZ
Le #797353
On 5 Aug 2004 14:33:16 -0700, (thitou):

merci fabien pour ces infos.


Tu te répètes...


--
;-)

Fabien LE LEZ
Le #797352
On 5 Aug 2004 14:30:26 -0700, (thitou):

Quel est le travail de make sous Linux ?


Le même que sous les autres OS.
Sa principale utilité est d'optimiser la compilation en ne compilant
que ce qui a été modifié. En gros, si dans un projet tu as 100 .cpp,
et que tu en modifies un, tu appriécies de pouvoir ne recompiler que
celui-là et pas les 99 autres.


--
;-)

kanze
Le #808266
(thitou) wrote in message
news:
C'est au sujet de la compilation d'un projet C++, que se passe t-il au
juste entre mon code et l'executable ?


Ça dépend de l'implémentation.

Pre-proceseur, compilateur, éditions de liens, j'en ai entendu parler,
mais concrétement je ne sais pas ce que ca fait.


Beaucoup.

La génération d'un exécutable à partir d'un ensemble de sources compte
pas mal de démarches. Et on peut le régarder de plusieurs façons : la
norme C++ définit 9 phases de translation, mais ces phases sont
conceptuelles, et je ne connais aucun compilateur qui fait un découpage
littéral selon ces 9 phases (qui n'ont pas de noms précis). Même la
signification des trois termes que tu cites varient.

En général, on peut découper la génération d'un exécutable en deux
groses parties : la « compilation » proprement dite, et l'édition de
liens. Mais quand la norme C++ parle de la « translation », il entend
l'ensemble, et quand tu invoque le « compilateur », en général,
l'édition de liens s'enchaîne automatiquement, de façon que l'outil que
tu connais sous le nom de compilateur en fait n'est qu'un programme de
pilotage qui invoque selon les besoins le compilateur proprement dit et
l'éditeur de liens. Sauf que dans la plupart des cas, il n'y a pas un
programme « compilateur proprement dit », mais plusieurs, chacun qui
s'occupe d'une phase (!= phase définie dans la norme) distincte -- avec
le C++, la même chose vaut pour l'éditeur de liens.

Et maintenant que tu es complètement confu, j'essaierai une réponse
pratique:-). En gros, quand on parle du « compilateur », si on ne réfère
pas au programme de pilotage, on parle de l'ensemble de programmes qui
sert à la compilation proprement dite, c-à-d à la traduction d'une seule
unité de compilation (.cpp, avec ces include) en un format intermédiaire
dit objet. Quand on parle de l'édition de liens, on parle de tout ce qui
suit, qui concerne (potentiellement, au moins) plusieurs « objets ». En
gros, toujours -- avec des templates, surtout, la distinction n'est pas
toujours aussi nette. (Donc, par exemple, dans plusieurs systèmes,
certains programmes de la phase d'édition de liens invoqueront le
compilateur proprement dit pour instantier les templates.)

En ce qui concerne le pré-processeur, c'est plutôt un concepte
historique -- aujourd'hui, le mot sert à désigner soit la partie du
langage défini dans le chapître 16 de la norme, soit (la plupart du
temps), les six premières phases de traduction définie dans la norme --
en gros, la résolution des macros et des includes, jusqu'au découpage de
la source en tokens significatifs pour le langage.

(Je ne sais pas ton niveau de connaissances en ce qui concerne le
vocabulaire des compilateurs. En gros, un token, c'est quelque chose qui
*pourrait* être séparé par des espaces, c-à-d que tu peux insérer des
espaces entre des tokens, mais non dans un token. Donc, en C++, « if »,
« intervallePrincipal », « x », « + » et « += » serait des tokens.
Parfois, il faut des espaces entre des tokens, pour savoir où un token
s'arrête, et l'autre commence, mais les espaces sont facultatifs si
c'est clair autrement. Et j'espère qu'il t'est clair que si j'écris
#define x a + c
au début du programme, lors de la compilation, un token « x » serait en
fait traiter comme une suite de trois tokens, « a », « + » et « b ».

Et s'il faut 6 phases dans la norme pour faire le « pré-processeur »,
c'est qu'il y a beaucoup de fonctionnalités particulières dans la
tokenisation de C++ : des trigraphes, des UCN, la concatenation des
chaînes littérales adjacentes... Et il faut bien définir l'ordre dans
lequel tout se fait. Dans la pratique, sauf dans les cas limites, le
considérer comme une seule phase est une simplification valable.)

Pour la reste, un compilation typique, disons invoquée par :
compile monprog.cpp quelquefonctions.cpp malib.lib

va se dérouler comme suit. (Remplacer « compile » par le nom de
l'exécutable de ton pilote préféré, g++, par exemple, ou cl pour VC++.
Et l'explication suivante ne correspond exactement à aucun compilateur
précis ; c'est juste une indication schématique du déroulement typique.)

phase de compilation (exécutée pour chacune des sources, la sortie d'une
démarche servant d'entrée pour la suivante) :

- Si le compilateur a un préprocesseur séparé (rarement le cas
aujourd'hui, je crois), le pilote l'invoque. Historiquement, le
résultat était un fichier texte, une source légale C++ sans
directifs de préprocesseur.

- Le pilote invoque la première passe (dite « front end ») du
compilateur. Typiquement, cette passe se découpe en analyse lexique
(résolution des directifs du préprocesseur et découpe de la source
en tokens) et analyse syntactique (création d'une représentation
interne du syntaxe de ton programme à partir des tokens).

- Si tu as démandé l'optimisation globale, le pilote invoque une phase
d'optimisation, qui « réarrange » la représentation interne.

- Ensuite, le pilote invoque la passe dite « back end », ou générateur
de code, sur la représentation interne. Cette passe determine, en
gros, quelles instructions machines qu'il faut -- la sortie peut
être soit en format objet direct, soit en assembleur. (Mais je crois
que la génération de l'assembleur est aussi en train de
disparaître.)

- Il peut y avoir encore une passe d'optimisation, dite « peep hole
optimisation », sur les instructions générées.

- Enfin, si le compilateur génère l'assembleur, le pilote invoque
l'assembleur pour générer l'objet.

Dans les fichiers d'objet, on rétrouve toutes les instructions machine
du programme. En révanche, les adresses en mémoire sont soit rélatives
(puisque le compilateur ne sait pas où le code dans le fichier objet va
réelement être situé dans le programme final), soit externe (encore
symbolique, puisque l'objet ou la fonction se trouve dans un autre
fichier objet.

Dans le phase d'édition de liens, le pilote prend les fichiers d'objet
générés par les compilations, les autres fichiers objet que tu aies pu
spécifier explicitement, les bibliothèques que tu as spécifiées, et
normalement aussi quelque bibliothèques systèmes aussi, et les passe à
l'éditeur de liens. L'éditeur de liens aligne les objets les uns après
les autres, détermine ainsi l'adresse du début de chaque objet (et donc,
l'adresse réele des symboles dans l'objet, ainsi que ce qu'il faut
ajouter aux adresses rélatives dans l'objet pour en avoir des adresses
physiques). Dans le cas d'une bibliothèque (qui est une collection
d'objets rassemblée par un programme spécial : « ar » sous Unix, ou «
lib » sous Windows), il selectionne uniquement les objets qui définisse
un symbole qui a servi, mais pour lequel il n'a pas de définition, et
ajoute ces objets au programme, exactement comme s'ils étaient spécifiés
sur la ligne de commande. Le résultat, c'est un fichier exécutable.

Il y a beaucoup de chose qui peut faire varier ce schéma. Je n'y ai pas
parlé de template, par exemple, parce que les compilateurs varient
énormement dans leur façon de les gérer. Dans les premières
implémentations, par exemple, le compilateur mettait de l'information
qu'il fallait pour pouvoir générer les instantiations dans un fichier à
part ; dans la phase de l'édition de liens, il y avait une passe
supplémentaire qui simulait l'édition de liens proprement dite,
déterminer les symboles qui manquaient, et chercher à les définir en
instantiant des templates en invoquant le compilateur avec ces fichiers
à part. Actuellement, la plupart des compilateurs Windows (je crois)
instantient les templates dans la phase de compilation, en mettant le
code généré directement dans le fichier objet (mais marqué spécialement,
pour que l'éditeur de liens sait que c'est l'instantiation d'un
template). L'éditeur de liens réconnaît les instantiations dupliquées,
et les jette. Beaucoup de compilateurs Unix met les instantiations dans
un répertoire à part (dite répositoire) ; dans ce cas-là, c'est le
compilateur qui réconnaît que l'instantiation est déjà présente, et ne
le régénère pas -- l'éditeur de liens traite ces répositoires à peu près
comme une bibliothèque supplémentaire.

Enfin, dans la plupart des systèmes, une partie de l'édition de liens
peut être différée à l'exécution, on parle alors d'une édition de liens
dynamique. En général, les éditions de liens différées ne peut pas faire
tout -- aucune aujourd'hui ne sait instantier un template, par exemple,
et je ne connais pas non plus qui sait traiter une bibliothèque. (Les
DLL de Windows se comporte comme des fichiers objets à cet égard, malgré
leur nom.)

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

kanze
Le #808265
(thitou) wrote in message
news:
Et comment est-ce que ca se passe avec des librairies compilées avec
le projet ?


Tout dépend. Quand tu crées une bibliothèque, typiquement, tu utilises
un programme spécial (« ar » sous Unix, « lib » sous Windows) à la place
de l'éditeur de liens. (Mais ce n'est pas toujours aussi simple -- à
cause des templates, le pilote Sun CC fait une fermature transitive sur
les répositoires de façon à ce que la bibliothèque contient toutes les
instantiations nécessaires.)

Si je comprend bien :

1- Preprocesseur (remplacement de texte)
2- Compilation des .cpp en .o
3- Edition de lien (avec les librairies ?)
4- L'exécutable est généré.


Voir mon autre posting. C'est un peu plus compliqué que ça.

Quel est le travail de make sous Linux ?


Le même que sous Windows:-).

Make est un programme en fait assez simple, qui gère les dépendences.
Donc, si ton programme contient trois fichiers sources, a.cpp, b.cpp et
c.cpp, l'éditeur de liens va avoir besoin de trois fichiers objet,
a.obj, b.obj et c.obj. Alors, make va savoir (à partir des informations
integrées dans le programme même et des informations que tu lui donnes)
1) que a.obj dépend de a.cpp, b.obj de b.cpp, c.obj de c.cpp et que le
programme final dépend de a.obj, b.obj et c.obj, et 2) ce qu'il faut
faire (les commandes du compilateur) pour générer un .obj à partir d'un
.cpp, et pour générer le programme à partir de a.obj, de b.obj et de
c.obj. Grosso modo, quand tu invoques make, il régarde les dates de la
dernière modification des fichiers, pour déterminer si par exemple,
a.obj est plus récent que a.cpp ou non, et le cas échéant, il
réconstruit les fichiers périmés.

Note que quand tu travailles avec une interface graphique, comme Visual
Studio, l'interface ne fait rien d'autre qu'invoquer make et des
programmes semblable.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Fabien LE LEZ
Le #799447
On 6 Aug 2004 02:28:44 -0700, :

En gros, un token, c'est quelque chose qui
*pourrait* être séparé par des espaces, c-à-d que tu peux insérer des
espaces entre des tokens, mais non dans un token. Donc, en C++, « if »,
« intervallePrincipal », « x », « + » et « += » serait des tokens.


Il me semble que certains compilos C acceptent un espace dans certains
tokens (du genre
x + = 4;
) -- mais je n'ai vu ça que sur le site de l'IOCCC.


--
;-)

kanze
Le #799218
Fabien LE LEZ news:
On 6 Aug 2004 02:28:44 -0700, :

En gros, un token, c'est quelque chose qui
*pourrait* être séparé par des espaces, c-à-d que tu peux insérer des
espaces entre des tokens, mais non dans un token. Donc, en C++, « if »,
« intervallePrincipal », « x », « + » et « += » serait des tokens.


Il me semble que certains compilos C acceptent un espace dans certains
tokens (du genre
x + = 4;
) -- mais je n'ai vu ça que sur le site de l'IOCCC.


De quelle année ?

Je crois que le pcc les acceptait (il y a vingt ans). Avant la norme, ce
n'était pas toujours clair si += était un seul token, ou deux ; la norme
C a tranché pour un seul, mais des compilateurs pré-norme variaient.

Et je ne crois pas que ce problème ait jamais affecté C++.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Poster une réponse
Anonyme