OVH Cloud OVH Cloud

new, et autre allocation memoire en programmation c++

27 réponses
Avatar
heinquoi
boujour,
je suis en train de maintéréssé la memoire et son utilisation en
programmation.
en c++ on defini plusieurs types de memoires (d'apres "c++:programmation
sous unix, windows et dos" de jesse Liberty & J. Mark Hord):
l'espace global de nom
l'espace memoire disponible ( ou tas)
les registres
l'espace de code
et la pile.
j'aimerais savoir à quoi ils correspondent au niveau materiel ou os.
pour les registres ... pas de problemes c'est au niveau processeur.
pour l'espace de code ... c'est la ou est le code du programme, sous win32,
entre 4Mo et 2Go ds le code programme.
pour les espaces de noms ...c'est la pile.(mais ou le compilateur difinie la
pile, et qui la gere?).
pour le tas ( alloué avec new), ou le compilateur défini le tas, et y a t il
un lien avec la mémoire paginé ?


si quelqu'un peux m'eclairer ?

--
Cordialement,
Heinquoi

10 réponses

1 2 3
Avatar
Brieuc Jeunhomme
<disclaimer>

Je ne sais pas si ce que je dis là correspond à quoi que ce soit dans le
norme, ce n'est qu'un résultat expérimental vrai partout pour autant que
je sache. Je ne parle que de ce qui se passe sous Unix, mais l'essentiel
doit être vrai sur la plupart des autres OS.

</disclaimer>

j'aimerais savoir à quoi ils correspondent au niveau materiel ou os.


Si tu as accès à un unix like, tu peux commencer par faire un objdump -h
sur un binaire au hasard. Voilà ce que ça donne chez moi avec objdump -h
/bin/sh:

Sections:
Idx Name Size VMA LMA File off Algn
0 .interp 00000013 08048134 08048134 00000134 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
[...]
11 .text 00069660 0805a6c0 0805a6c0 000126c0 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
[...]
13 .rodata 00017990 080c3d40 080c3d40 0007bd40 2**5
CONTENTS, ALLOC, LOAD, READONLY, DATA
[...]
15 .data 00004ed8 080dc000 080dc000 00094000 2**5
CONTENTS, ALLOC, LOAD, DATA
[...]
22 .bss 00004774 080e1320 080e1320 00099320 2**5
ALLOC

Cette commande affiche les différentes sections qui vont être placées en
mémoire au lancement du programme (attention, cette manipulation est
très liée au format ELF, utilisé sur la plupart des unix) et leur
adresse de début (VMA).

Tu peux notamment voir les sections .text (où sera placé le code du
programme), .bss, où seront placées les variables statiques et globales
non initialisées au lancement du programme, la section .text, où sera
placé le code exécutable, la section .rodata, où tu trouveras les
variables globales ou statiques constantes, et les chaînes de caractères
anonymes, et la section .data, où tu trouveras les autres variables
statiques ou constantes. Tout ceci est (je crois) décidé par l'éditeur
de liens.

Ensuite, le tas se trouve sur beaucoup d'implémentations Unix après la
section .bss, mais ce n'est en rien une obligation. D'autre part, il est
souvent séparé en deux parties: la région brk() pour les petits buffers
(souvent située après la bss) et la région mmap() pour les buffers plus
gros, et dont l'adresse est déterminée par le noyau du système lors des
appels à mmap(). Autrement dit, l'allocateur de mémoire de ton système,
pour les allocations sur le tas, utilise les appels brk(), sbrk(), et
mmap(), et le kernel décide où il va mettre tout ça.

Enfin, la pile, utilisée pour les variables automatiques, c'est le noyau
qui décide où elle va.

L'expérience montre que sur un noyau donné, toutes ces régions
commencent toujours aux mêmes adresses, sauf dans quelques situations
particulières (ie. versions récentes d'OpenBSD, ou Linux avec les
patches pax et/ou grsecurity).

Enfin, si ça t'intéresse de voir ce que ça donne sur différents OS et
différentes architectures, tu peux faire un tour sur

http://people.via.ecp.fr/~bbp/sysnumbers/

(fin de la séquence publicitaire).

--
BBP

Avatar
Jacti

boujour,
je suis en train de maintéréssé la memoire et son utilisation en
programmation.
en c++ on defini plusieurs types de memoires (d'apres "c++:programmation
sous unix, windows et dos" de jesse Liberty & J. Mark Hord):
l'espace global de nom
l'espace memoire disponible ( ou tas)
les registres
l'espace de code
et la pile.
j'aimerais savoir à quoi ils correspondent au niveau materiel ou os.
pour les registres ... pas de problemes c'est au niveau processeur.
pour l'espace de code ... c'est la ou est le code du programme, sous win32,
entre 4Mo et 2Go ds le code programme.
pour les espaces de noms ...c'est la pile.(mais ou le compilateur difinie la
pile, et qui la gere?).
pour le tas ( alloué avec new), ou le compilateur défini le tas, et y a t il
un lien avec la mémoire paginé ?

si quelqu'un peux m'eclairer ?


Les compilateurs placent le tas où ils veulent, ils gèrent la pile
comme ils veulent, pour peu qu'ils respectent la sémantique du langage,
bref, quand on utilise un langage de haut niveau ce n'est pas pour se
préoccuper de ces choses bassement matérielles.
La seule chose qui compte pour le programmeur, c'est ce qui est
dit dans la norme du langage, pas ce que font les différents OS
et les différents compilateurs sur différentes machines..... qui
doivent respecter la norme.

Par exemple, un compilateur C++ peut très bien, pour une variable
auto dépassant une certaine taille mémoire, mettre seulement un pointeur
dans la pile afin de ne pas "l'encombrer" et allouer la variable "ailleurs".
Evidemment, le compilateur "s'arrangera" pour que l'utilisateur ait
"l'impression" que la variable est dans la pile. Elle sera effectivement
comme une variable "auto". Il y aura cependant une indirection
pour l'adresser dans l'assembleur généré.

De même les variables "static" sont en général regroupées (en distinguant
cependant les variables "extern").
Elles sont allouées par le linker.

Jacti (qui a fait des compilateurs)

Avatar
heinquoi
"Jacti" a écrit dans le message de
news:
Les compilateurs placent le tas où ils veulent, ils gèrent la pile
comme ils veulent, pour peu qu'ils respectent la sémantique du langage,
bref, quand on utilise un langage de haut niveau ce n'est pas pour se
préoccuper de ces choses bassement matérielles.


effectivement...mais un doute ma assaillis!! Concernant la capacité de mes
compilateurs à optimiser leur accès à la mémoire virtuelle. Cela est sans
doute dû au cours d'architecture des machines que j'ai suivi....comme quoi
avoir des réponses posent encore plus de nouvelles questions.
En fait les pages de mémoire sous x86 sont de 4Ko, et lors d'allocation, le
compilateur par intermédiaire de l'os va s'alloué des pages en fonction des
besoins et fractionner ses pages si les données sont < à 4ko.Et ainsi
remplir sa page avec des données de petites tailles.

La seule chose qui compte pour le programmeur, c'est ce qui est
dit dans la norme du langage, pas ce que font les différents OS
et les différents compilateurs sur différentes machines..... qui
doivent respecter la norme.


En effet. Cela dit puisque la norme confie la gestion de la pile au
compilateur, quel est intérêt d'utiliser la mémoire dynamique ( tas) ? Le
programmeur devrait pouvoir créer des données a volonté. Je ne pense pas que
le seul critère de choix de l'utilisation de new ( tas) soit la porté de ses
objets au delà de la fin de leur espace de nom.

Par exemple, un compilateur C++ peut très bien, pour une variable
auto dépassant une certaine taille mémoire, mettre seulement un pointeur
dans la pile afin de ne pas "l'encombrer" et allouer la variable
"ailleurs".

Évidemment, le compilateur "s'arrangera" pour que l'utilisateur ait
"l'impression" que la variable est dans la pile. Elle sera effectivement
comme une variable "auto". Il y aura cependant une indirection
pour l'adresser dans l'assembleur généré.


je ne pensais pas cela possible !! Il me trompe! La différence entre le tas
et la pile sont donc plus conceptuelle que physique.
Une autre question me vient: Il y a t'il un lien direct entre la pile dont
on parle et la pile utilisé par push et pop ( qui en fait sont utilisé pour
le passage de paramètre aux fonctions ) et dont le haut est pointé par le
registre SP ?

De même les variables "static" sont en général regroupées (en distinguant
cependant les variables "extern").
Elles sont allouées par le linker.

Jacti (qui a fait des compilateurs)
respect ! faire des compilos cela me semble très pointu..


--
Cordialement,
Heinquoi

Avatar
Jacti

"Jacti" a écrit dans le message de
news:
Les compilateurs placent le tas où ils veulent, ils gèrent la pile
comme ils veulent, pour peu qu'ils respectent la sémantique du langage,
bref, quand on utilise un langage de haut niveau ce n'est pas pour se
préoccuper de ces choses bassement matérielles.


effectivement...mais un doute ma assaillis!! Concernant la capacité de mes
compilateurs à optimiser leur accès à la mémoire virtuelle. Cela est sans
doute dû au cours d'architecture des machines que j'ai suivi....comme quoi
avoir des réponses posent encore plus de nouvelles questions.
En fait les pages de mémoire sous x86 sont de 4Ko, et lors d'allocation, le
compilateur par intermédiaire de l'os va s'alloué des pages en fonction des
besoins et fractionner ses pages si les données sont < à 4ko.Et ainsi
remplir sa page avec des données de petites tailles.


Pour optimiser l'accès à la mémoire virtuelle, il faut surtout des disques durs
rapides !!

Après, le fractionnement des pages, c'est le problème de l'OS et du compilateur
(qui fait des fois ce qu'il peut en fonction des possibilité du gestionnaire de
mémoire virtuelle de l'OS...).

La seule chose qui compte pour le programmeur, c'est ce qui est
dit dans la norme du langage, pas ce que font les différents OS
et les différents compilateurs sur différentes machines..... qui
doivent respecter la norme.


En effet. Cela dit puisque la norme confie la gestion de la pile au
compilateur, quel est intérêt d'utiliser la mémoire dynamique ( tas) ? Le
programmeur devrait pouvoir créer des données a volonté. Je ne pense pas que
le seul critère de choix de l'utilisation de new ( tas) soit la porté de ses
objets au delà de la fin de leur espace de nom.


Justement, c'est l'allocation dynamique (new) qui permet au programmeur
de créer des données à volonté à l'exécution du programme.
Par exemple, pour un système de fenêtrage, l'objet fenêtre va être alloué
dynamiquement car on ne sait pas, a priori, combien l'utilisateur va en ouvrir.
Cependant, certains systèmes mettent des limites

Ta dernière phrase est ambigüe car elle semble mélanger le type d'allocation
et la visibilité (scope en anglais), ce qui n'est pas du tout la même chose.

Par exemple, un compilateur C++ peut très bien, pour une variable
auto dépassant une certaine taille mémoire, mettre seulement un pointeur
dans la pile afin de ne pas "l'encombrer" et allouer la variable
"ailleurs".

Évidemment, le compilateur "s'arrangera" pour que l'utilisateur ait
"l'impression" que la variable est dans la pile. Elle sera effectivement
comme une variable "auto". Il y aura cependant une indirection
pour l'adresser dans l'assembleur généré.


je ne pensais pas cela possible !! Il me trompe! La différence entre le tas
et la pile sont donc plus conceptuelle que physique.


Dit autrement, c'est de la sémantique. Peu importe comment
le compilateur gère les différents types d'allocation mémoire du moment
qu'il respecte la sémantique du langage à l'exécution.

Une autre question me vient: Il y a t'il un lien direct entre la pile dont
on parle et la pile utilisé par push et pop ( qui en fait sont utilisé pour
le passage de paramètre aux fonctions ) et dont le haut est pointé par le
registre SP ?


C'est quoi push et pop ? des instruction assembleur du processeur machin,
des opérations du type abstrait "Pile", des ordres du langage intermédieur issus

de la phase d'analyse sémantique, du P-Code "à la Pascal", des instructions
d'une machine virtuelle "langage", etc. ??

C'est quoi le registre SP ? je soupçonne que ce soit Stack Pointer :-)
mais ça ne s'appelle pas comme ça dans tous les environnements...
Et puis il y a des machines qui n'ont pas de mécanisme "hardware" de gestion de
pile.
Dans ce cas c'est le compilateur qui doit générer ce qu'il faut pour simuler
une pile...


De même les variables "static" sont en général regroupées (en distinguant
cependant les variables "extern").
Elles sont allouées par le linker.

Jacti (qui a fait des compilateurs)
respect ! faire des compilos cela me semble très pointu..



Disons que ce n'est pas tout simple ;-)

Jacti


Avatar
heinquoi
--
Cordialement,
Heinquoi
"Jacti" a écrit dans le message de
news:
Justement, c'est l'allocation dynamique (new) qui permet au programmeur
de créer des données à volonté à l'exécution du programme.
Par exemple, pour un système de fenêtrage, l'objet fenêtre va être alloué
dynamiquement car on ne sait pas, a priori, combien l'utilisateur va en
ouvrir.

Cependant, certains systèmes mettent des limites
Ta dernière phrase est ambigüe car elle semble mélanger le type
d'allocation

et la visibilité (scope en anglais), ce qui n'est pas du tout la même
chose.


Je n'ai tjrs pas saisi l'interet de passer par new pour l'allocation
memoire...je peux tout aussi bien creer mes données a volonté pdt
l'execution du programme dans la pile, c'est meme + simple car j'ai pas à
gere la destruction.Et j'ai le sentiment que sur un x86 de type 80686 ou +,
avec windows 2K, et avec VC++ ou intel c++, je n'aurait pas plus de
limitation d'espace memoire dans la pile que dans le tas. Et que dit la
norme sur ce probleme des differentes mémoire ?

Dit autrement, c'est de la sémantique. Peu importe comment
le compilateur gère les différents types d'allocation mémoire du moment
qu'il respecte la sémantique du langage à l'exécution.


sauf que plus je sais ce que fait le compilo et plus je suis capable
d'atteindre le programme que j'avais imaginé.
par rapport a ce que je souhaite obtenir, si je sais ce que fait le compilo
alors c'est plus facile pour moi pour faire ce que je veux.Plus je discocie
qui fait quoi de l'os et du compilo et plus j'ai une vue globale tout en
restant précis.
cela dit on peux tjrs ce referer à la norme, mais je crois de plus en plus
que ce n'est pas suffisant.Tout les compilateurs ne l'ayant pas complètement
et precisement implementé.Et elle dit ce que doit etre capable de renvoyer
le compilo, mais elle ne dit pas qui le fait du compilo ou de l'os.

Une autre question me vient: Il y a t'il un lien direct entre la pile
dont


on parle et la pile utilisé par push et pop ( qui en fait sont utilisé
pour


le passage de paramètre aux fonctions ) et dont le haut est pointé par
le


registre SP ?


C'est quoi push et pop ? des instruction assembleur du processeur machin,
des opérations du type abstrait "Pile", des ordres du langage intermédieur
issus


de la phase d'analyse sémantique, du P-Code "à la Pascal", des
instructions

d'une machine virtuelle "langage", etc. ??


Je pensais a push et pop les instruction assembleur que les processeur x86
ont en hard.

C'est quoi le registre SP ? je soupçonne que ce soit Stack Pointer :-)
mais ça ne s'appelle pas comme ça dans tous les environnements...
Et puis il y a des machines qui n'ont pas de mécanisme "hardware" de
gestion de

pile.
Dans ce cas c'est le compilateur qui doit générer ce qu'il faut pour
simuler

une pile...
Et la je pensait effectivement au registre des Processeurs x86 famille 386

et + qui ont un registre 32 bits, ESP = Extended Stark Pointer qui pointe
sur le haut d'une pile ( pile que les instructions asm PUSH et POP
utilisent.Instruction elle meme allègrement utilisé par les compilateurs
c/c++)
Donc d'apres ta réponse et si je comprend bien, il ne s'agit pas de la meme
pile.
Pour la serie x86:Il y a une pile utilisé par le processeur, et une autre
utilisé par le compilateur.L'une peut etre dans l'autre mais ce sont 2
abstractions diferentes.

--
Cordialement,
Heinquoi


Avatar
Arnaud Meurgues
heinquoi wrote:

en c++ on defini plusieurs types de memoires (d'apres "c++:programmation
sous unix, windows et dos" de jesse Liberty & J. Mark Hord):
l'espace global de nom
l'espace memoire disponible ( ou tas)
les registres
l'espace de code
et la pile.


Selon la norme, il n'y a rien de tout cela. Il y a de la mémoire qui est
un ensemble de mots contigüs ou non ayant chacun une adresse.
Ensuite, les objets sont une "région de stockage" qui a une "durée de
stockage":
- statique
- automatique
- dynamique

Je ne vois pas en C++ (dans la norme) de tas, de pile, de registre,
d'espace de code ou d'espace global de nom.

Dans la pratique, les objets à durée de stockage statique peuvent être
dans un "espace global de nom", les objets à durée de stockage
automatique, dans la pile ou dans des registres et les objets à durée de
stockage dynamique, dans le tas. Les fonctions du programme pouvant être
dans un "espace de code".

Mais le C++ ne s'intéresse pas à ces détails d'implémentation. Ce qu'il
défini, c'est ce qu'est un programme bien formé et quel doit être son
comportement observable. Ensuite, n'importe quelle implémentation qui
donne le bon comportement observable (celui défini par la norme) à un
programme C++ bien formé est valide.

pour les espaces de noms ...c'est la pile.(mais ou le compilateur difinie la
pile, et qui la gere?).


En général, la pile est la pile de programme et est donc définie à la
fois par le processeur (qui a une façon propre de gérer une pile) et par
l'OS (qui, notamment dans le cas des OS multitâches, doit s'arranger
pour que chaque processus ait sa pile (et chaque thread aussi le cas
échéant)).

pour le tas ( alloué avec new), ou le compilateur défini le tas, et y a t il
un lien avec la mémoire paginé ?


En général, oui. C'est à dire que le tas est une zone de mémoire
adressable et qu'il est maintenant fréquent de paginer cette mémoire, ce
qui permet de gérer plus facilement du multitasking d'une part (en
protégeant les pages de chaque processus contre les accès des autres
processus), et de la mémoire virtuelle d'autre part (en permettant
d'adresser plus de mémoire que celle physiquement présente et en
swappant sur disque les pages peu utilisées).

--
Arnaud
(Supprimez les geneurs pour me répondre)

Avatar
Arnaud Meurgues
heinquoi wrote:

En effet. Cela dit puisque la norme confie la gestion de la pile au
compilateur,


Où ça ?

quel est intérêt d'utiliser la mémoire dynamique ( tas) ? Le
programmeur devrait pouvoir créer des données a volonté. Je ne pense pas que
le seul critère de choix de l'utilisation de new ( tas) soit la porté de ses
objets au delà de la fin de leur espace de nom.


Ah bon ? Pour moi, c'est au contraire *le* critère.
Et la norme C++ le dit clairement dans [3.7/2] :

"
3.7/2
Static and automatic storage durations are associated with objects
introduced by declarations (3.1) and implicitly created by the
implementation (12.2). The dynamic storage duration is associated with
objects created with operator new (5.3.4)
"

operator new est fait pour les objets à durée de stockage dynamique.

Mais effectivement, si l'on n'a jamais besoin d'objet ayant une durée de
vie supérieure au scope où ils sont créés, alors il n'est pas utile
d'utiliser new.

je ne pensais pas cela possible !! Il me trompe! La différence entre le tas
et la pile sont donc plus conceptuelle que physique.


Il y a une vraie différence sémantique. Un objet sur une pile ne peut
pas avoir une durée de vie supérieure à la fonction qui l'y met parce
que, à la sortie de la pile, tout est dépilé. Et celà, c'est une
contrainte "physique" lié au fonctionnement de la pile.
Mais C++ ne raisonne pas en terme de pile et si l'on veut implémenter
les durées de stockages automatiques autrement, on est libre de le
faire, l'essentiel étant que le comportement observable d'un programme
bien formé respecte la norme.

Une autre question me vient: Il y a t'il un lien direct entre la pile dont
on parle et la pile utilisé par push et pop ( qui en fait sont utilisé pour
le passage de paramètre aux fonctions ) et dont le haut est pointé par le
registre SP ?


Je ne connais pas de cas où ce n'est pas la même pile (ce serait se
compliquer la vie pour pas grand chose). Mais le C++ ne l'impose pas.

--
Arnaud
(Supprimez les geneurs pour me répondre)

Avatar
Mickael Pointier
Une autre question me vient: Il y a t'il un lien direct entre la pile
dont


on parle et la pile utilisé par push et pop ( qui en fait sont utilisé
pour


le passage de paramètre aux fonctions ) et dont le haut est pointé par
le


registre SP ?


Je ne connais pas de cas où ce n'est pas la même pile (ce serait se
compliquer la vie pour pas grand chose). Mais le C++ ne l'impose pas.


Je n'ai pas de compilateur C++ sur mon Oric, mais j'ai un compilateur C, et
c'est bien le cas sur celui-ci (et je suppose que ca aurait été le même
choix pour un compilateur C++).

Vu que le 6502 dispose d'une pile processeur de maximum 256 octets, les
adresses de retour sont bien empilées sur celle-ci, par contre pour
permettre d'avoir des variables locales de taille décente, il y a en plus la
gestion d'une pile logicielle par le compilo, qui elle peut faire jusqu'a
64ko.

Mike


Avatar
Jean-Marc Bourguet
"heinquoi" <nospam* writes:

Je n'ai tjrs pas saisi l'interet de passer par new pour l'allocation
memoire...


Ca permet de creer des structures dynamiques. De la liste au graphe.

je peux tout aussi bien creer mes données a volonté pdt l'execution
du programme dans la pile, c'est meme + simple car j'ai pas à gere
la destruction. Et j'ai le sentiment que sur un x86 de type 80686
ou +, avec windows 2K, et avec VC++ ou intel c++, je n'aurait pas
plus de limitation d'espace memoire dans la pile que dans le tas.


Je ne sais pas si Windows limite la pile. Dos le faisait, Unix le
fait toujours (c'est reglable mais ici, c'est 8M si je ne regle rien).

Et que dit la norme sur ce probleme des differentes mémoire ?


Rien. La norme definit un comportement. L'implementation se
debrouille pour le fournit. Il y a parfois des termes communs entre
les deux, mais c'est rare que ca designe exactement la meme chose et
qu'il y ait une correspondance obligee.

Dit autrement, c'est de la sémantique. Peu importe comment le
compilateur gère les différents types d'allocation mémoire du
moment qu'il respecte la sémantique du langage à l'exécution.


sauf que plus je sais ce que fait le compilo et plus je suis capable
d'atteindre le programme que j'avais imaginé.


Moi je concois le programme en termes abstraits, l'implemente en terme
des langages utilises et laisse -- generalement -- faire les
implementations de ces langages. Et je te conseille tant que tu
n'auras pas plus d'experience de faire comme ca (si tu ne vois pas
l'interet des structures dynamiques, c'est certain que tu n'as pas
l'experience necessaire).

par rapport a ce que je souhaite obtenir, si je sais ce que fait le compilo
alors c'est plus facile pour moi pour faire ce que je veux.Plus je discocie
qui fait quoi de l'os et du compilo et plus j'ai une vue globale tout en
restant précis.
cela dit on peux tjrs ce referer à la norme, mais je crois de plus en plus
que ce n'est pas suffisant.Tout les compilateurs ne l'ayant pas complètement
et precisement implementé.Et elle dit ce que doit etre capable de renvoyer
le compilo, mais elle ne dit pas qui le fait du compilo ou de l'os.


Faut pas exagerer, les problemes a cause de choses non completement
implementees sont rares et affectent les gens utilisant des techniques
hors de ta competance actuelle.

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
heinquoi
merci Arnaud pour ta clarté et ta précision.c'est que sans ton post je
serais encore à cherché dans des bouquins ou sur le net. Bonne idée de
m'avoir répondu.
--
Cordialement,
Heinquoi
1 2 3