web-dev-qa-db-fra.com

C++ efface un pointeur (mémoire libre)

Considérons le code suivant:

int a = 10;
int * b = &a;
int * c = b;
delete b; // equivalent to delete c;

Ai-je raison de comprendre à la dernière ligne, delete b et delete c sont équivalents, et que les deux libéreront l'espace mémoire contenant a, donc a ne sera plus accessible?

11
space_voyager

Le comportement de votre programme est undefined . Vous pouvez seulement utiliser delete sur un pointeur vers la mémoire que vous avez alloué en utilisant new. Si tu avais écrit 

int* b = new int;
*b = 10;
int* c = b;

alors vous pourriez écrire soit delete b; ou delete c; pour libérer votre mémoire. N'essayez pas de derefererence soit b ou c après l'appel delete, le comportement est également undefined .

42
Bathsheba

Si b et c pointent vers la même mémoire, la suppression de l'un d'eux libère la mémoire de sorte que l'hypothèse est correcte. a devenir inaccessible n’est pas correct dans ce cas, car vous ne pointez pas sur la mémoire allouée dynamiquement et vous ne pouvez appeler que delete/delete[] sur quelque chose qui a été créé avec new/new[]. Essayer delete/delete[] un pointeur qui n'a pas été alloué avec new/new[] est un comportement non défini et aboutira généralement à une erreur de segmentation.

5
NathanOliver

La partie déroutante est que la réponse à votre question

Ai-je raison de comprendre à la dernière ligne, supprimer b et supprimer c sont équivalents "

Si oui, ils sont équivalents, et les deux UB comme mentionné partout ailleurs ici.

5
UKMonkey

Cette réponse particulière utilise une mise en œuvre particulière du tas pour entrer dans les détails de ce qui se passerait dans la situation proposée; Cependant, cela importe peu, car d'autres versions auraient encore des problèmes similaires proches de l'argument que je présente.

La réponse est "non", et c'est à cause de ce que d'autres ont dit. Cependant, je vais être un peu plus précis, car je pense que vous ne savez pas vraiment ce que sont la pile et la pile et je vais donc prendre le temps de vous expliquer cela.

Pour commencer, tout ce que je dis en termes de détails sur le tas est un "mensonge". Personne ne sait précisément ce que votre compilateur fait pour implémenter le tas. Donc, je ne peux que donner ma compréhension de a heap à la place.

Nouveau et supprimer sont en fait deux fonctions (si vous avez entendu parler de la surcharge d'opérateur, vous comprendrez ce que je veux dire par là) qui acceptent les pointeurs et modifient une structure de données en mémoire connue sous le nom de segment de mémoire. C'est l'une des quatre structures de données principales de la mémoire d'un programme C/C++ (le tas, la pile, le texte et l'espace statique).

Le tas est essentiellement une liste chaînée, mais au lieu de stocker des données dans chaque nœud, vous devez les stocker entre eux. Si vous n'êtes pas familier avec une liste chaînée, dans ce contexte, il s'agit d'un "tableau" à deux composants situé dans la mémoire brute. Le deuxième composant est la longueur du bloc stocké devant lui. Le premier composant est la longueur du bloc qui le précède. De cette manière, il est facile de trouver un bloc car il suffit simplement de descendre dans la liste pour rechercher un bloc. Habituellement (parce que les programmes ne sont même pas assez complexes pour nécessiter 2 ^ 31 octets alloués dans un seul bloc), le bit le plus à gauche est utilisé comme valeur vrai/faux pour indiquer si le bloc est libre. Par conséquent, free et new ne sont que des opérations sur la liste et free sauvegarde simplement le pointeur vers le haut pour examiner le nœud et apporter les modifications appropriées. Je n’entrerai pas plus dans les détails, car ceux qui sont familiers avec les listes doublement chaînées devraient comprendre les opérations. Si vous ne comprenez pas, il ne sert à rien d’essayer de vous apprendre quelque chose que vous apprendrez beaucoup plus tard par la suite.

Souvenez-vous: vos implémentations varient en fonction du compilateur. C'est juste un exemple.

La pile sur l'autre est où sont vos variables locales. C'est littéralement un tableau géant de tableaux. Le milieu du tableau contient l'adresse dans le code sur laquelle le programme doit retourner après l'exécution, puis les paramètres sont d'un côté, tandis que les variables locales sont de l'autre. Les variables locales n'ont pas de sens clair de l'organisation. Certains compilateurs peuvent détecter qu'une valeur cesse d'être utilisée avant une autre et ne partage que l'emplacement de la mémoire (vous ne le remarquerez jamais à moins que vous ne jetiez un coup d'œil en mémoire ou que vous n'ayez pas analysé la pile).

Comme vous pouvez le constater, les deux n’ont tout simplement aucun sens. Appeler free sur un pointeur indexant la pile signifie que vous essayez d'effectuer l'opération de liste sur la pile. Une des deux choses va se passer:

  1. Votre implémentation détecte que la mémoire n’est même pas dans le tas et explose avec un message d’erreur (exception d’argument non conforme).

  2. L'implémentation est "stupide" et tente en réalité d'effectuer l'opération de liste. Cela signifie que la valeur avant la variable en mémoire sera modifiée et que d'autres valeurs pourraient être modifiées dans le but de "fusionner" deux blocs libres en un bloc plus grand.

En termes simples: ne faites pas ça. Vous pourriez casser quelque chose dans votre programme. Et si vous bousiller la ligne de retour? La prochaine chose que vous savez, vous auriez encore une boucle infinie d’appels de fonction ... vous n’écriviez aucune boucle. Très très mauvais.

Et bien sur:

Votre implémentation exacte du tas varie énormément, contrairement à la pile qui n'a pas tendance à changer beaucoup.

Par exemple, bien que je n’aie jamais vu le code ou un schéma de la mémoire brute d’une telle structure, j’ai entendu dire qu’il existe des modèles de l’espace de tas construits autour de deux listes distinctes contenant les blocs libérés et les blocs alloués. Cela n’aurait aucune incidence sur la question, car il existe toujours une forme de comptabilité interne qui traite de la répartition.

0
The Great Duck

vous ne devriez jamais utiliser delete pour la variable allouée sur la pile, delete est la contrepartie de new . Par conséquent, aucune suppression/libération n'est nécessaire si aucun nouveau/alloc n'est utilisé, dès que vous êtes hors de la "portée" de le code (dans ce cas le programme lui-même) toute la mémoire est considérée disponible.

0
Nir Levy