Supprimer un pointeur en C ++

91

Contexte: J'essaie de comprendre les pointeurs, nous les avons vus il y a quelques semaines à l'école et en pratiquant aujourd'hui, je suis tombé sur un idiot? problème, cela peut être très simple pour vous, mais j'ai peu ou pas d'expérience en programmation.

J'ai vu pas mal de questions dans SO sur la suppression de pointeurs, mais elles semblent toutes liées à la suppression d'une classe et non d'un pointeur `` simple '' (ou quel que soit le terme approprié), voici le code que j'essaie de courir:

#include <iostream>;

using namespace std;

int main() {
  int myVar,
      *myPointer;

  myVar = 8;
  myPointer = &myVar;

  cout << "delete-ing pointers " << endl;
  cout << "Memory address: " << myPointer << endl;

  // Seems I can't *just* delete it, as it triggers an error 
  delete myPointer;
  cout << "myPointer: " << myPointer << endl;
  // Error: a.out(14399) malloc: *** error for object 0x7fff61e537f4:
  // pointer being freed was not allocated
  // *** set a breakpoint in malloc_error_break to debug
  // Abort trap: 6

  // Using the new keyword befor deleting it works, but
  // does it really frees up the space? 
  myPointer = new int;
  delete myPointer;
  cout << "myPointer: " << myPointer << endl;
  // myPointer continues to store a memory address.

  // Using NULL before deleting it, seems to work. 
  myPointer = NULL;
  delete myPointer;
  cout << "myPointer: " << myPointer << endl;
  // myPointer returns 0.

}

Donc mes questions sont:

  1. Pourquoi le premier cas ne fonctionnera-t-il pas? Semble l'utilisation la plus simple pour utiliser et supprimer un pointeur? L'erreur indique que la mémoire n'a pas été allouée mais «cout» a renvoyé une adresse.
  2. Sur le deuxième exemple, l'erreur n'est pas déclenchée mais faire un cout de la valeur de myPointer renvoie toujours une adresse mémoire?
  3. Le n ° 3 fonctionne-t-il vraiment? Cela me semble fonctionner, le pointeur ne stocke plus d'adresse, est-ce la bonne façon de supprimer un pointeur?

Désolé pour la longue question, je voulais que cela soit aussi clair que possible, aussi pour le répéter, j'ai peu d'expérience en programmation, donc si quelqu'un pouvait répondre à cette question en utilisant des termes simples, ce serait grandement apprécié!

léopique
la source
16
La raison pour laquelle vous ne voyez pas le premier exemple est qu'il est faux. Seulement deletece que vous new. Il n'est pas non plus nécessaire que le pointeur se définisse sur NULL après l'avoir supprimé. Si vous voulez la sécurité là-bas, utilisez des pointeurs intelligents, qui libèrent la mémoire pour vous et donnent des erreurs lorsque vous essayez d'y accéder alors qu'ils ne contiennent pas quelque chose.
chris
Hmm d'accord, je ne suis pas sûr de ce que sont les pointeurs intelligents, mais je vais l'examiner, merci!
léopique
1
En un mot, ils font ce que j'ai décrit. Afin de contenir quelque chose de nouveau, vous appelez resetet cela libère l'ancien. Pour le libérer sans remplacement, vous appelez release. Lorsqu'il est hors de portée, il est détruit et peut libérer la mémoire en fonction de son type. std::unique_ptrest destiné à un seul propriétaire. std::shared_ptrle libère lorsque le dernier propriétaire cesse de posséder la ressource. Ils sont également exceptionnellement sûrs. Si vous allouez une ressource avec une, puis rencontrez une exception, la ressource sera correctement libérée.
chris

Réponses:

165

1 et 2

myVar = 8; //not dynamically allocated. Can't call delete on it.
myPointer = new int; //dynamically allocated, can call delete on it.

La première variable a été allouée sur la pile. Vous ne pouvez appeler delete que sur la mémoire que vous avez allouée dynamiquement (sur le tas) à l'aide de l' newopérateur.

3.

  myPointer = NULL;
  delete myPointer;

Ce qui précède n'a rien fait du tout . Vous n'avez rien libéré, comme le pointeur pointait sur NULL.


Ce qui suit ne doit pas être fait:

myPointer = new int;
myPointer = NULL; //leaked memory, no pointer to above int
delete myPointer; //no point at all

Vous l'avez pointé sur NULL, laissant derrière vous une fuite de mémoire (le nouvel int que vous avez alloué). Vous devez libérer la mémoire que vous pointez. Il n'y a plus moyen d'accéder à ce qui est alloué new int, d'où une fuite de mémoire.


La bonne manière:

myPointer = new int;
delete myPointer; //freed memory
myPointer = NULL; //pointed dangling ptr to NULL

La meilleure façon:

Si vous utilisez C ++, n'utilisez pas de pointeurs bruts. Utilisez plutôt des pointeurs intelligents qui peuvent gérer ces choses pour vous avec peu de frais généraux. C ++ 11 est livré avec plusieurs fichiers .

Anirudh Ramanathan
la source
13
<pedantry> "On the stack" est un détail d'implémentation - un détail que C ++ évite de mentionner. Le terme le plus correct est "avec durée de stockage automatique". (C ++ 11, 3.7.3) </
pedantry
4
Merci, j'ai choisi votre réponse pour a) expliquer ce qui n'allait pas et b) donner une meilleure pratique, merci beaucoup!
leopic
6
@Tqn Ce n'est pas juste. delete myPointerdésalloue *myPointer. C'est correct. Mais myPointercontinue de pointer vers un emplacement mémoire qui a été libéré et ne doit pas être utilisé car il s'agit d'UB. Il ne sera inaccessible après la fin de la portée que SI c'était une variable locale en premier lieu.
Anirudh Ramanathan
2
@DarkCthulhu Merci! (J'apprends littéralement) quelque chose newtous les jours. (I'm cheesy!)
Tqn
1
@AmelSalibasic La mémoire associée à la variable sur la pile ne sera libérée qu'une fois qu'elle sera hors de portée. L'attribuer à NULLnous empêche de l'utiliser à mauvais escient plus tard.
Anirudh Ramanathan
23

Je pense que vous ne comprenez pas parfaitement le fonctionnement des pointeurs.
Lorsque vous avez un pointeur pointant vers une mémoire, il y a trois choses différentes que vous devez comprendre:
- il y a "ce qui est pointé" par le pointeur (la mémoire)
- cette adresse mémoire
- tous les pointeurs n'ont pas besoin d'avoir leur mémoire supprimée: vous seulement besoin de supprimer la mémoire allouée dynamiquement ( newopérateur utilisé ).

Imaginer:

int *ptr = new int; 
// ptr has the address of the memory.
// at this point, the actual memory doesn't have anything.
*ptr = 8;
// you're assigning the integer 8 into that memory.
delete ptr;
// you are only deleting the memory.
// at this point the pointer still has the same memory address (as you could
//   notice from your 2nd test) but what inside that memory is gone!

Quand tu as fait

ptr = NULL;
// you didn't delete the memory
// you're only saying that this pointer is now pointing to "nowhere".
// the memory that was pointed by this pointer is now lost.

C ++ vous permet d'essayer deleteun pointeur qui pointe nullmais il ne fait rien, ne donne simplement aucune erreur.

salgadokk
la source
2
Merci, CECI a été super utile, je pensais que je devais supprimer tous les pointeurs, je ne savais pas que c'était uniquement pour ceux qui étaient nouveaux, merci.
leopic
13

Les pointeurs sont similaires aux variables normales en ce sens que vous n'avez pas besoin de les supprimer. Ils sont supprimés de la mémoire à la fin de l'exécution d'une fonction et / ou à la fin du programme.

Vous pouvez cependant utiliser des pointeurs pour allouer un 'bloc' de mémoire, par exemple comme ceci:

int *some_integers = new int[20000]

Cela allouera de l'espace mémoire pour 20000 entiers. Utile, car la pile a une taille limitée et vous voudrez peut-être vous amuser avec une grosse charge d '«ints» sans erreur de débordement de pile.

Chaque fois que vous appelez new, vous devez alors «supprimer» à la fin de votre programme, car sinon vous obtiendrez une fuite de mémoire, et une partie de l'espace mémoire alloué ne sera jamais retournée à d'autres programmes. Pour faire ça:

delete [] some_integers;

J'espère que cela pourra aider.

user3728501
la source
1
Je veux juste ajouter que la mémoire allouée sera retournée pour d'autres programmes à utiliser, mais seulement APRÈS que votre programme ait fini de s'exécuter.
sk4l
7

Il existe une règle en C ++, pour chaque nouvelle il y a une suppression .

  1. Pourquoi le premier cas ne fonctionnera-t-il pas? Semble l'utilisation la plus simple pour utiliser et supprimer un pointeur? L'erreur indique que la mémoire n'a pas été allouée mais «cout» a renvoyé une adresse.

nouveau n'est jamais appelé. Ainsi, l'adresse que cout imprime est l'adresse de l'emplacement mémoire de myVar, ou la valeur affectée à myPointer dans ce cas. En écrivant:

myPointer = &myVar;

vous dites:

myPointer = L'adresse de l'emplacement de stockage des données dans myVar

  1. Sur le deuxième exemple, l'erreur n'est pas déclenchée mais faire un cout de la valeur de myPointer renvoie toujours une adresse mémoire?

Il renvoie une adresse qui pointe vers un emplacement mémoire qui a été supprimé. Parce que vous créez d'abord le pointeur et attribuez sa valeur à myPointer, ensuite vous le supprimez, troisièmement vous l'imprimez. Donc, sauf si vous attribuez une autre valeur à myPointer, l'adresse supprimée restera.

  1. Le n ° 3 fonctionne-t-il vraiment? Cela me semble fonctionner, le pointeur ne stocke plus d'adresse, est-ce la bonne façon de supprimer un pointeur?

NULL est égal à 0, vous supprimez 0, donc vous ne supprimez rien. Et c'est logique qu'il affiche 0 parce que vous l'avez fait:

myPointer = NULL;

ce qui équivaut à:

myPointer = 0;
fonZ
la source
4
  1. Vous essayez de supprimer une variable allouée sur la pile. Tu ne peux pas faire ça
  2. La suppression d'un pointeur ne détruit pas réellement un pointeur, seule la mémoire occupée est restituée au système d'exploitation. Vous pouvez y accéder jusqu'à ce que la mémoire soit utilisée pour une autre variable ou autrement manipulée. Il est donc recommandé de définir un pointeur sur NULL (0) après la suppression.
  3. La suppression d'un pointeur NULL ne supprime rien.
Hakan Serce
la source
1
int value, *ptr;

value = 8;
ptr = &value;
// ptr points to value, which lives on a stack frame.
// you are not responsible for managing its lifetime.

ptr = new int;
delete ptr;
// yes this is the normal way to manage the lifetime of
// dynamically allocated memory, you new'ed it, you delete it.

ptr = nullptr;
delete ptr;
// this is illogical, essentially you are saying delete nothing.
Casper Beyer
la source
1
En outre, consultez cette conférence sur les cadres de pile youtube.com/watch?v=bjObm0hxIYY et youtube.com/watch?v=Rxvv9krECNw sur les pointeurs.
Casper Beyer