OVH Cloud OVH Cloud

Question sur la compilation

58 réponses
Avatar
Michaël Delva
Bonjour à tous,

j'ai une petite question sur la compilation:

J'ai un fichier .H où sont déclarées 3 fonctions A, B et C, et le .CPP où
se trouvent leurs implémentations.

J'ajoute le .CPP au projet, et dans le code du projet je n'utilise pas la
fonction C.

Quel est le comportement du compilateur: est-ce que la fonction C est quand
même incluse dans l'EXE?

Je me pose la question car avec mon compilo (Borland 6) quand je suis en
mode debug je ne peux pas ajouter de points d'arrets sur le code des
fonctions ou classes que je n'utilise pas.

Et enfin (plus spécifique Windows), est-ce le même principe pour les DLLs?
(Mais pourquoi ne serait-ce pas le cas?)

Merci d'avance...

10 réponses

1 2 3 4 5
Avatar
Michel Michaud
Dans news:,
j'ai une petite question sur la compilation:

J'ai un fichier .H où sont déclarées 3 fonctions A, B et C, et
le .CPP où se trouvent leurs implémentations.

J'ajoute le .CPP au projet, et dans le code du projet je
n'utilise pas la fonction C.

Quel est le comportement du compilateur: est-ce que la fonction
C est quand même incluse dans l'EXE?


Le compilateur ne fait pas le programme exécutable, c'est
l'éditeur de liens qui s'en occupe. Pour le compilateur, les
fonctions sont peut-être utilisées, alors il doit les produire
dans le code objet (.o, .obj, ...).

Ensuite l'éditeur de liens voit ce qui est vraiment nécessaire
et, selon ses capacités, mettra ou non le code inutile dans
l'exécutable final. Historiquement les éditeurs de liens
incluaient le code sur la base des fichiers objets complets
et c'est pourquoi on faisait souvent, pour les bibliothèques,
une fonction par fichier source. Aujourd'hui il existe des
éditeurs de liens plus intelligents, capables de faire la
part des choses :-)

Je me pose la question car avec mon compilo (Borland 6) quand
je suis en mode debug je ne peux pas ajouter de points d'arrets
sur le code des fonctions ou classes que je n'utilise pas.

Et enfin (plus spécifique Windows), est-ce le même principe
pour les DLLs? (Mais pourquoi ne serait-ce pas le cas?)


Tout ce qu'on pourrait dire aujourd'hui pourrait être faux
demain, car il n'existe aucune norme officielle sur ce qui doit
être fait dans un environnement ou un autre. On sort ici du
cadre C++ et on rentre dans celui de la « qualité de
l'implémentation », encore que ce soit plus celui de l'éditeur
de liens probablement. Mais pour les DLL, les choses sont
tellement différentes qu'on est encore plus loin du C++...

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/

Avatar
Pierre Maurette
"Michaël Delva" typa:

Bonjour à tous,

j'ai une petite question sur la compilation:

J'ai un fichier .H où sont déclarées 3 fonctions A, B et C, et le .CPP où
se trouvent leurs implémentations.

J'ajoute le .CPP au projet, et dans le code du projet je n'utilise pas la
fonction C.

Quel est le comportement du compilateur: est-ce que la fonction C est quand
même incluse dans l'EXE?
Mon opinion, sans certitude absolue : la norme ne connait par de

compilateur (ni même d'exe), mais une "translation" (code source ->
comportement).

La réalité n'est pas la norme. Dans les environnements courants que je
connais (le même que le votre, en particulier, mais en fait ce que
j'écris est très général), ce n'est pas le compilateur qui fabrique
l'exe.

Le compilateur effectue la "translation", et à ma connaissance il
traduit toutes les fonctions. Peut-être y a-t-il des execptions
(static ?), mais dans votre cas il génèrera du code objet pour la
fonction C. Le compilateur ignore la cible, le travail futur du lieur.
Il est d'ailleurs logique qu'il fabrique toutes les fonctions,
puisqu'à l'inverse il n'a pas besoin d'avoir le code (source ou objet)
de toutes les fonctions appelées. Je pense que le compilateur génère
entre autres choses, pour chaque fonction, le code machine à la
relocation près et la liste des fonctions appelables.

Le lieur fabrique une cible. Dans le cas que vous proposez, la cible
est un simple exécutable, qui exige un main(), il n'y a aucune raison
qu'il incorpore le code de la fonctiuon C. (Je suppose que) Le lieur
part du main() et de sa liste de fonctions pour bâtir de proche en
proche une liste globale de fonctions nécessaires.

Renommez main() en pied(). Le compilateur s'en moque, c'est le lieur
qui va se plaindre parce que la cible exige un main(). En revanche,
s'il y a un main() à compiler, le compilateur connait bien entendu les
règles qui s'y appliquent.


Je me pose la question car avec mon compilo (Borland 6) quand je suis en
mode debug je ne peux pas ajouter de points d'arrets sur le code des
fonctions ou classes que je n'utilise pas.
J'en suis très étonné.




Et enfin (plus spécifique Windows), est-ce le même principe pour les DLLs?
(Mais pourquoi ne serait-ce pas le cas?)
Le compilateur ne sait pas plus ce qu'est une DLL que ce qu'est un

exécutable. Il n'y a pas de différence avec le cas précédent, il va
compiler des fonctions. La cible du lieur sera une DLL, il aura donc
besoin de générer (donc de trouver dans le code objet) les fonctions
DllMain(), DllEntryPoint(), et les fonctions déclarées exportées de la
DLL. Ne seront dans le fichier .dll que ces fonctions et celles qui
leur sont nécessaires, comme dans le cas précédent.
Si vos fonctions A, B et C sont dans votre idée les fonctions
exportables de la DLL, la question n'a pas de sens. Elles seront
toutes trois "dans la DLL". Elles seront ensuite appelées à la
demande, soit de façon explicite (le programme récupère un pointeur
sur chaque fonction exportée qu'il souhaite utiliser, après avoir
chargé la DLL), soit de façon implicite, en laissant Windows se
démerder, en gros, comme on utilise les fonctions API, avec un .h, un
.lib, et un .dll.

--
Pierre

Avatar
Arnaud Debaene
Michaël Delva wrote:
Bonjour à tous,

j'ai une petite question sur la compilation:

J'ai un fichier .H où sont déclarées 3 fonctions A, B et C, et le
.CPP où se trouvent leurs implémentations.

J'ajoute le .CPP au projet, et dans le code du projet je n'utilise
pas la fonction C.

Quel est le comportement du compilateur: est-ce que la fonction C est
quand même incluse dans l'EXE?


Ca dépend de l'éditeur de liens.

Je me pose la question car avec mon compilo (Borland 6) quand je suis
en mode debug je ne peux pas ajouter de points d'arrets sur le code
des fonctions ou classes que je n'utilise pas.
Je dirais qu'il est possible qu'en mode debug, l'éditeur de liens soit

"bête" et inclue tout le code, même non utilisé, et qu'en release il essaie
d'être un peu plus malin et supprime le code inutile. Mais pour être sûr, il
faut regarder la doc de l'editeur de lien (chercher une option "suppression
du coe inutile" ou équivalent)

Et enfin (plus spécifique Windows), est-ce le même principe pour les
DLLs? (Mais pourquoi ne serait-ce pas le cas?)
C'est le même principe, sauf que les fonctions exportées ne sont évidemment

jamais supprimées, même si elles ne sont pas utilisées dans la DLL elle
même.

Arnaud

Avatar
Michaël Delva
Merci à tous les 3 pour vos éclaircissements... Je connais un peu mieux
désormais les étapes de fabrication d'un projet...

Encore merci!
Avatar
James Kanze
"Arnaud Debaene" writes:

|> Michaël Delva wrote:

|> > j'ai une petite question sur la compilation:

|> > J'ai un fichier .H où sont déclarées 3 fonctions A, B et C, et le
|> > .CPP où se trouvent leurs implémentations.

|> > J'ajoute le .CPP au projet, et dans le code du projet je n'utilise
|> > pas la fonction C.

|> > Quel est le comportement du compilateur: est-ce que la fonction C
|> > est quand même incluse dans l'EXE?

|> Ca dépend de l'éditeur de liens.

Sauf que typiquement, on n'invoque pas un « compilateur » ni un
«@éditeur de liens ». On invoque un programme pilote, du genre CC, g++
ou cl depuis la ligne de commande (si ce n'est pas carrément make), ou
on on clique sur une entrée dans un menu. Et les compilations et
l'édition de liens s'enchaîne.

En fait, dans ce cas-ci, ça dépend surtout sur ce qu'on a démandé au
système de compilation de faire. Je peux arriver facilement à l'un ou
l'autre résultat, selon ce que je veux, avec tous les systèmes que je
connais.

Comment dire au système qu'on veux que la module x fasse partie du
programme inconditionnellement, ou qu'elle n'en fasse partie que si elle
résoud une lien externe non résolu autrement dépend de l'implémentation.
En général, si on incorpore le fichier objet, elle est inclue de façon
inconditionnelle, et si on le met dans une bibliothèque, et on incorpore
la bibliothèque, elle n'est inclue que conditionnellement. Mais comment
faire qu'elle se trouve dans une bibliothèque, ou qu'on en prend l'objet
directement, dépend une fois de plus de l'implémentation.

[...]
|> > Et enfin (plus spécifique Windows), est-ce le même principe pour
|> > les DLLs? (Mais pourquoi ne serait-ce pas le cas?)

|> C'est le même principe, sauf que les fonctions exportées ne sont
|> évidemment jamais supprimées, même si elles ne sont pas utilisées
|> dans la DLL elle même.

C'est le même principe, parce que malgré le nom, les DLL ne sont pas des
bibliothèque, mais des fichiers objets monolithiques.

--
James Kanze
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
Avatar
kanze
Pierre Maurette wrote in message
news:...
"Michaël Delva" typa:

j'ai une petite question sur la compilation:

J'ai un fichier .H où sont déclarées 3 fonctions A, B et C, et le
.CPP où se trouvent leurs implémentations.

J'ajoute le .CPP au projet, et dans le code du projet je n'utilise
pas la fonction C.

Quel est le comportement du compilateur: est-ce que la fonction C est
quand même incluse dans l'EXE?


Mon opinion, sans certitude absolue : la norme ne connait par de
compilateur (ni même d'exe), mais une "translation" (code source ->
comportement).


Tout à fait. En fait, la norme parle des « implémentations du langage de
programmation C++ ». Qui comprend aussi la bibliothèque et
l'environement lors de l'exécution.

La réalité n'est pas la norme. Dans les environnements courants que je
connais (le même que le votre, en particulier, mais en fait ce que
j'écris est très général), ce n'est pas le compilateur qui fabrique
l'exe.

Le compilateur effectue la "translation", et à ma connaissance il
traduit toutes les fonctions. Peut-être y a-t-il des execptions
(static ?), mais dans votre cas il génèrera du code objet pour la
fonction C. Le compilateur ignore la cible, le travail futur du lieur.
Il est d'ailleurs logique qu'il fabrique toutes les fonctions,
puisqu'à l'inverse il n'a pas besoin d'avoir le code (source ou objet)
de toutes les fonctions appelées. Je pense que le compilateur génère
entre autres choses, pour chaque fonction, le code machine à la
relocation près et la liste des fonctions appelables.

Le lieur fabrique une cible. Dans le cas que vous proposez, la cible
^^^^^


Je croyais qu'on disait « éditeur de liens » en français. Quand on ne
dit pas carrément « linker »:-).

est un simple exécutable, qui exige un main(), il n'y a aucune raison
qu'il incorpore le code de la fonctiuon C. (Je suppose que) Le lieur
part du main() et de sa liste de fonctions pour bâtir de proche en
proche une liste globale de fonctions nécessaires.

Renommez main() en pied(). Le compilateur s'en moque, c'est le lieur
qui va se plaindre parce que la cible exige un main(). En revanche,
s'il y a un main() à compiler, le compilateur connait bien entendu les
règles qui s'y appliquent.


C'est une bonne explication général de ce qui se passe couramment.
Plusieurs détails, cépendant :

- En général, ce qu'on invoque (cl sous Windows, CC ou g++ sous Unix)
est en fait un programme pilote qui enchaîne les démarches, et qui
invoque à la fois le compilateur et l'éditeur de liens. Par un abus
du language, on parle souvent d'invoquer le compilateur quand on
l'invoque, et de « compiler » un programme pour en générer
l'exécutable.

- La norme ne divise pas la tranduction en deux phases (compilation et
édition de liens), mais en 9. Historiquement, la plupart des
« compilateurs » executait le préprocesseur comme phase
préliminaire, à part, et même aujourd'hui, beaucoup de compilateurs
ne génère que de l'assembleur. (Mais on peut considérer tout ça
comme des détails de l'implémentation.)

- Dans les implémentations les plus modernes et les plus performantes,
la distinction compilation/édition de liens devient flou. En fait,
pour faire une bonne optimisation, il faut savoir le contexte où la
fonction a été appelé. Du coup, le compilateur se contente de
générer un espèce de code intermédiaire, et c'est l'éditeur de liens
qui s'occupe de l'optimisation finale et la transformation en code
machine.

Je ne connais pas de compilateur Windows (ni Linux ni Mac) à ce
niveau, mais j'ai entendu dire que c'est peut-être le cas pour VC++
7.1. De toute façon, ça viendra.

Enfin, la norme parle des unités de traduction -- en gros, une unité de
traduction, c'est une source principale avec tous ces include. La norme
ne prévoit pas la possibilité de n'incorpore qu'une partie d'une unité
de traduction dans ton programme. En revanche, elle ne spécifie
absolument rien en ce qui concerne comment spécifier quelles unités de
traductions font partie de tel ou tel programme.

Traditionnellement, et dans la plupart des systèmes encore aujourd'hui,
la règle est simple : si on spécifie un fichier objet lors de l'édition
de liens, il fait partie du programme. Si en revanche, on met l'objet
dans une bibliothèque, et on spécifie la bibliothèque, le fichier objet
ne fait partie du programme que sous certaines conditions -- en général,
il faut qu'il résoud un symbole externe qui ne serait pas résolu
autrement. (Mais les détails de comment ça marche varie énormement, et
les systèmes Unix fonctionnent bien différemment que Windows à cet
égard.)

Note que de ce point de vue, des DLL ne sont pas des bibliothèques, mais
des fichiers objet.

Je me pose la question car avec mon compilo (Borland 6) quand je suis
en mode debug je ne peux pas ajouter de points d'arrets sur le code
des fonctions ou classes que je n'utilise pas.


J'en suis très étonné.


L'erreur classique ici, c'est d'avoir mis l'objet dans une bibliothèque.
Alors, si on ne le référence pas directement, il ne fait pas partie du
programme. Et s'il ne fait pas partie du programme, c'est normal que le
debugger ne trouve pas les fonctions qu'il contient.

Et enfin (plus spécifique Windows), est-ce le même principe pour les
DLLs? (Mais pourquoi ne serait-ce pas le cas?)


Le compilateur ne sait pas plus ce qu'est une DLL que ce qu'est un
exécutable. Il n'y a pas de différence avec le cas précédent, il va
compiler des fonctions. La cible du lieur sera une DLL, il aura donc
besoin de générer (donc de trouver dans le code objet) les fonctions
DllMain(), DllEntryPoint(), et les fonctions déclarées exportées de la
DLL. Ne seront dans le fichier .dll que ces fonctions et celles qui
leur sont nécessaires, comme dans le cas précédent. Si vos fonctions
A, B et C sont dans votre idée les fonctions exportables de la DLL, la
question n'a pas de sens. Elles seront toutes trois "dans la DLL".
Elles seront ensuite appelées à la demande, soit de façon explicite
(le programme récupère un pointeur sur chaque fonction exportée qu'il
souhaite utiliser, après avoir chargé la DLL), soit de façon
implicite, en laissant Windows se démerder, en gros, comme on utilise
les fonctions API, avec un .h, un .lib, et un .dll.


Plus simplement : une DLL est un grand objet qui résulte de l'édition de
liens d'un certain nombre d'objets plus petits. Malgré son nom, il n'est
PAS une bibliothèque -- ou bien, une DLL fait partie du programme, avec
tous les objets qui ont servi à le créer, ou bien, elle ne fait pas
partie du tout.

--
James Kanze GABI Software
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


Avatar
Pierre Maurette
typa:
[...]
Le lieur fabrique une cible. Dans le cas que vous proposez, la cible
^^^^^


Je croyais qu'on disait « éditeur de liens » en français. Quand on ne
dit pas carrément « linker »:-).
Tiens, c'est marrant, Microsoft est 100% "Editeur de liens", Borland

100% "Lieur". Je n'avais pas remarqué.
J'essaie d'être constant, mais de là à être cohérent... "Lieur", mais
"Librarian", par exemple.
Que désigner par le mot "assembleur" ? J'avais tendance à dire que
c'est un ensemble d'outils, comprenant un compilateur de langage
assembleur. Mais ça ne fonctionne pas chez GNU, qui utilise beaucoup
d'outils dans son processus de fabrication. La raison en est claire,
elle est dans la multiplicité des processeurs cible. Ce n'est
certainement favorable ni à la rapidité de construction, ni à
l'optimisation.
A l'inverse, VC7.1 semble avoir une structure sous-jacente très
différente de la structure visible (wrappers en nombre). J'ai remarqué
que le même code source donnait lieu à la génération de plusieurs
séquences binaires. J'avais d'abord pensé que le choix était fait à
l'exécution, sans arriver à comprendre le code, je pense maintenant
que c'est plutôt à l'édition de liens. En fait, j'avais certainement
confondu l'exe désassemblé et le .asm correspondant au .obj. Il me
faudra creuser ça à l'occasion.
Borland traite (semble-t-il) le code source directement pour fabriquer
du code objet. Les sorties préprocesseur et assembleur sont des
"dérivations".

Note que de ce point de vue, des DLL ne sont pas des bibliothèques, mais
des fichiers objet.
Je n'utilisais pas le même vocabulaire pour "objet", mais je pense que

l'idée est la même. Je distingue (en C/C++/asm):
- Le code source, éditable : sources, sorties asm et préprocesseur par
exemple.
- Le code objet, du code machine aux références non résolues, non lié.
Fichiers .obj intermédiaires, fichiers .lib contenant des fonctions à
lier statiquement, ...
- Le code exécutable, c'est à dire du code lié plus tout ce qu'il faut
pour en faire un fichier correspondant à la cible (divers formats de
.exe, plugins, DLL, et bien d'autres).

Je me pose la question car avec mon compilo (Borland 6) quand je suis
en mode debug je ne peux pas ajouter de points d'arrets sur le code
des fonctions ou classes que je n'utilise pas.


J'en suis très étonné.


L'erreur classique ici, c'est d'avoir mis l'objet dans une bibliothèque.
Alors, si on ne le référence pas directement, il ne fait pas partie du
programme. Et s'il ne fait pas partie du programme, c'est normal que le
debugger ne trouve pas les fonctions qu'il contient.
La question était peut-être mal posée. L'OP parlait d'un fichier .CPP

contenant les implémentations des fonctions A, B et C. Pas grave.
--
Pierre



Avatar
Alain Naigeon
"Pierre Maurette" a écrit dans le message news:


Borland traite (semble-t-il) le code source directement pour fabriquer
du code objet. Les sorties préprocesseur et assembleur sont des
"dérivations".


Il me semble que sur la plupart des compilateurs actuels la sortie
"assembleur" n'est que du pseudo-assembleur reconstitué à partir
des structures de données internes. Autrement dit, cette sortie
mise en forme pour homo sapiens ne sert pas à la compilation
par la suite.
En revanche il est clair que la phase préprocesseur -> C++ source
"ordinaire" est indispensable à la compilation.
Enfin voilà, c'est ce que j'intuitionne à une heure aussi précoce
de la journée :-)

--

Français *==> "Musique renaissance" <==* English
midi - facsimiles - ligatures - mensuration
http://anaigeon.free.fr | http://www.medieval.org/emfaq/anaigeon/
Alain Naigeon - - Strasbourg, France

Avatar
Loïc Joly
wrote:

Traditionnellement, et dans la plupart des systèmes encore aujourd'hui,
la règle est simple : si on spécifie un fichier objet lors de l'édition
de liens, il fait partie du programme. Si en revanche, on met l'objet
dans une bibliothèque, et on spécifie la bibliothèque, le fichier objet
ne fait partie du programme que sous certaines conditions -- en général,
il faut qu'il résoud un symbole externe qui ne serait pas résolu
autrement. (Mais les détails de comment ça marche varie énormement, et
les systèmes Unix fonctionnent bien différemment que Windows à cet
égard.)


Ce qui peut être assez gênant. Quelqu'un sait-il pourquoi la norme
laisse de côté ces points qui pourtant peuvent avoir une influence sur
la sémantique d'un programme ?

--
Loïc

Avatar
Pierre Maurette
"Alain Naigeon" typa:

"Pierre Maurette" a écrit dans le message news:


Borland traite (semble-t-il) le code source directement pour fabriquer
du code objet. Les sorties préprocesseur et assembleur sont des
"dérivations".


Il me semble que sur la plupart des compilateurs actuels la sortie
"assembleur" n'est que du pseudo-assembleur reconstitué à partir
des structures de données internes. Autrement dit, cette sortie
mise en forme pour homo sapiens ne sert pas à la compilation
par la suite.
GNU : il suffit d'enlever (ou de renommer) 'as' ou 'as.exe' et de

lire:
g++: installation problem, cannot exec `as': No such file or directory

En revanche il est clair que la phase préprocesseur -> C++ source
"ordinaire" est indispensable à la compilation.
Enfin voilà, c'est ce que j'intuitionne à une heure aussi précoce
de la journée :-)
Pas intuitionnationner, se référer à doc ;-). Borland explique

clairement qu'il n'y a pas réellement de phase isolable de
précompilation dans leur processus de fabrication du code objet mené
de façon autonome par bcc32.exe et que cpp32.exe est fourni comme
outil indépendant, pour par exemple préprocesser des macros.
--
Pierre


1 2 3 4 5