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
kanze
Pierre Maurette wrote in message
news:...
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é.


Intéressant. C'est la première fois que j'entends « lieur ».

Peut-être le mieux alors, c'est « linker ». Au moins comme ça, on est
sûr que tout le monde nous comprend:-).

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.


Le programme assembleur est au langage assembleur ce qu'est un
compilateur au langage évolué. Il se place dans un ensemble d'outils
(éditeur de liens, bibliothèquaire, etc.), de la même façon que se place
un compilateur. Au moins sous Unix, les « compilateurs » comprennent
habituellement un assembleur, et l'assembleur peut être invoqué par le
même pilote que le compilateur -- si j'invoque « g++ toto.s », il traite
toto.s comme une source assembleur, et n'invoque que l'assembleur (et
l'éditeur de liens). Aussi, au moins dans le cas de g++, le compilateur
génère l'assembleur, plutôt que de l'objet classique ; le pilote invoque
donc l'assembleur après chaque passe du compilateur.

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.


Tout à fait. En fait, l'architecture de gcc me semble assez ancienne.
Dans le temps, tous les compilateurs Unix avaient un préprocesseur
séparé, et généraient l'assembleur. Aujourd'hui, au moins, Sun CC n'a
plus de préprocesseur séparé ; je n'arrive pas à savoir au juste en ce
qui concerne l'assembleur -- il ne l'invoque pas, au moins pas ce que je
vois, mais il y a bien un temporaire de type .s qui apparaît, puis
disparaît. Sous Unix aussi, les éditeurs de liens ne comprenent pas
C++ ; du coup, on a une commande supplémentaire de prélinkage.

Ce n'est pas la multiplicité des processeurs cible qui le veut. Que tu
génère l'assembleur, ou que tu génère l'objet direct, le généré dépend
bien du processeur ciblé.

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.


C'est vrai pour prèsque tous les compilateurs, je crois. Déjà, si tu
précises l'optimisation, tu aurais quelque chose de différent que sans
l'optimisation. Puis, il y a des options qui concerne l'alignement, si
char est signé ou non, si le code est indépendant de la position ou non,
s'il est réentrant...

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.


Je ne comprends pas. De quelles différences parles-tu ? L'éditeur de
liens peut choisir entre une bibliothèque ou une autre, mais il choisit
pas entre deux versions du même code dans un seul fichier objet.

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".


C'est le cas de la plupart des compilateurs modernes.

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).


Donc, les DLL sont bien des « objets », parce qu'elles ne sont pas
directement exécutable ; il faut les linker avec d'autre chose d'abord.

En fait, une bibliothèque n'est autre chose qu'une collection de
fichiers objet. Avec la différence, par rapport à spécifier tous les
fichiers qu'il contient comme fichier objet, que l'éditeur de liens ne
prend que ce qu'il veut d'une bibliothèque.

--
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
kanze
"Alain Naigeon" wrote in message
news:<40bde9ad$0$20059$...
"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.


C'est vrai pour les compilateurs Windows, je crois. C'est moins vrai
pour les compilateurs Unix -- g++, en tout cas, invoque réelement
l'assembleur chaque fois que tu compiles.

En revanche il est clair que la phase préprocesseur -> C++ source
"ordinaire" est indispensable à la compilation.


Pas du tout. Sun CC ne le fait pas ; je crois que g++ non plus. Et ça
m'étonnera que les compilateurs Windows le fasse. Une phase
préprocesseur séparé est très coûteuse en temps de compilation ; il
exige de faire le même boulot (la découpe en tokens) deux fois.

--
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
kanze
Loïc Joly wrote in message
news:<c9l5cq$fk8$...
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 ?


Parce que la façon d'invoquer un programme et de lui passer des
paramètres dépend de toute façon étroitement du système où on se trouve.
Sous Unix, par exemple, j'ai typiquement toutes ses informations dans
mes fichiers de make, que j'édite avec vim ou emacs. Sous Windows, avec
Visual Studio, il est plus fréquent de définir un projet avec
l'interface graphique. Comment veux-tu formuler quoique ce soit qui
permet des modèles de spécification aussi différents ?

--
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
Gabriel Dos Reis
writes:

[...]

| > En revanche il est clair que la phase préprocesseur -> C++ source
| > "ordinaire" est indispensable à la compilation.
|
| Pas du tout. Sun CC ne le fait pas ; je crois que g++ non plus. Et ça
| m'étonnera que les compilateurs Windows le fasse. Une phase
| préprocesseur séparé est très coûteuse en temps de compilation ; il
| exige de faire le même boulot (la découpe en tokens) deux fois.

Dans les versions antérieures de GCC (i.e. avant 3.0), il y avait une
phase séparée de preprocessing. Le préprocesseur a été complètement
intégré aux front-ends (C et C++) dans les versions après 3.0.

Par exemple, dans les anciennes versions de GCC, si tu invoques cc1plus
sur du source C++ qui contient des commentaires, il devient
complètement confusé et t'insulte en disant qu'il y a erreur de
syntaxe. Ce n'est plus le cas maintenant. Aussi avant, tu pouvais
remarquer un gain de temps non néglieable avec l'option -pipe qui
court-circuite la création de fichier temporaire pour communiquer
entre les différentes phases de compilation.

-- Gaby
Avatar
Alain Naigeon
a écrit dans le message news:

Une phase
préprocesseur séparé est très coûteuse en temps de compilation ; il
exige de faire le même boulot (la découpe en tokens) deux fois.


Je te crois évidemment, mais j'ai du mal à comprendre :
1) je repère et je développe les directives commençant
par un #, et elles seules, et je les intègre au reste qui
n'est pas du tout analysé ;
2) l'analysateur du compilateur reçoit ce source, et
commence la "tokenisation".

Je ne vois pas bien ce que j'ai fait en double, où est
donc mon erreur ?
En fait je commence à voir : ce n'est pas l'analyse
qui est faite en double, mais tout de même je
parcours deux fois le code, la première fois pour
repérer les #.

--

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

Avatar
Jean-Marc Bourguet
"Alain Naigeon" writes:

a écrit dans le message news:

Une phase
préprocesseur séparé est très coûteuse en temps de compilation ; il
exige de faire le même boulot (la découpe en tokens) deux fois.


Je te crois évidemment, mais j'ai du mal à comprendre :
1) je repère et je développe les directives commençant
par un #, et elles seules, et je les intègre au reste qui
n'est pas du tout analysé ;


Et les utilisations de macros?

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
Christophe de VIENNE
a écrit dans le message news:


Une phase
préprocesseur séparé est très coûteuse en temps de compilation ; il
exige de faire le même boulot (la découpe en tokens) deux fois.



Je te crois évidemment, mais j'ai du mal à comprendre :
1) je repère et je développe les directives commençant
par un #, et elles seules, et je les intègre au reste qui
n'est pas du tout analysé ;


Tu oublie les substitutions dues aux #define, qui oblige à une
tokenisation, même élémentaire.


Avatar
Gabriel Dos Reis
"Alain Naigeon" writes:

| a écrit dans le message news:
|
| > Une phase
| > préprocesseur séparé est très coûteuse en temps de compilation ; il
| > exige de faire le même boulot (la découpe en tokens) deux fois.
|
| Je te crois évidemment, mais j'ai du mal à comprendre :
| 1) je repère et je développe les directives commençant
| par un #, et elles seules, et je les intègre au reste qui
| n'est pas du tout analysé ;
| 2) l'analysateur du compilateur reçoit ce source, et
| commence la "tokenisation".
|
| Je ne vois pas bien ce que j'ai fait en double, où est
| donc mon erreur ?
| En fait je commence à voir : ce n'est pas l'analyse
| qui est faite en double, mais tout de même je
| parcours deux fois le code, la première fois pour
| repérer les #.

Faire un pré-processsing séparé veut dire que dans un premier temps,
tu as découpé le flux de caractères (le programme) en suite de
préprocessing tokens et que tu as encore sorti une suite de
caractères (typiquement ce qui arrive si tu invoques g++ -E).
Ensuite, le front-end doit encore re-tokeniser ta sortie pour pouvoire
faire l'analyse syntaxique et sémantique.

-- Gaby
Avatar
Matthieu Moy
"Alain Naigeon" writes:

a écrit dans le message news:

Une phase
préprocesseur séparé est très coûteuse en temps de compilation ; il
exige de faire le même boulot (la découpe en tokens) deux fois.


Je te crois évidemment, mais j'ai du mal à comprendre :
1) je repère et je développe les directives commençant
par un #, et elles seules, et je les intègre au reste qui
n'est pas du tout analysé ;
2) l'analysateur du compilateur reçoit ce source, et
commence la "tokenisation".


#define a b

int main() {
int a = 42;
}

Le préprocesseur va remplacer le `a' de "a = 42", mais pas le `a' de
"main", et pour ça, il faut "tokeniser".

Par contre, le temps de compil n'est quand même pas essentiellement du
au découpage en jetons, après, il y a l'analyse syntaxique, le typage,
les optims et la génération de code qui sont quand même
particulièrement longs.

--
Matthieu


Avatar
Loïc Joly
wrote:
Loïc Joly wrote in message

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 ?



Parce que la façon d'invoquer un programme et de lui passer des
paramètres dépend de toute façon étroitement du système où on se trouve.
Sous Unix, par exemple, j'ai typiquement toutes ses informations dans
mes fichiers de make, que j'édite avec vim ou emacs. Sous Windows, avec
Visual Studio, il est plus fréquent de définir un projet avec
l'interface graphique. Comment veux-tu formuler quoique ce soit qui
permet des modèles de spécification aussi différents ?


Je ne parlais pas de la syntaxe des appels, mais de la spécification du
comportement de la phase de lien.

On défini déjà la notion de fichier objet et d'édition de lien. Le point
qui ne m'apparait pas clair, c'est la façon dont on choisi les variables
et fonctions qui peuvent disparaitre.

A moins que ce soit ma lecture du standardese qui soit déficiente,
"Library components are linked to satisfy external
references to functions and objects not defined in the current
translation." me semble un peu vague.

Je n'apprécie en particulier pas qu'un même fichier objet inclus
directement ou par l'intermédiaire d'une bibliothèque puisse produire
des exécutables aux comportements différents.

--
Loïc


1 2 3 4 5