Un livre C ++ que j'ai lu indique que lorsqu'un pointeur est supprimé à l'aide de l' delete
opérateur, la mémoire à l'emplacement vers lequel il pointe est "libérée" et peut être écrasée. Il indique également que le pointeur continuera à pointer vers le même emplacement jusqu'à ce qu'il soit réaffecté ou défini sur NULL
.
Dans Visual Studio 2012 cependant; cela ne semble pas être le cas!
Exemple:
#include <iostream>
using namespace std;
int main()
{
int* ptr = new int;
cout << "ptr = " << ptr << endl;
delete ptr;
cout << "ptr = " << ptr << endl;
system("pause");
return 0;
}
Lorsque je compile et exécute ce programme, j'obtiens la sortie suivante:
ptr = 0050BC10
ptr = 00008123
Press any key to continue....
Il est clair que l'adresse vers laquelle pointe le pointeur change lorsque la suppression est appelée!
Pourquoi cela arrive-t-il? Cela a-t-il quelque chose à voir avec Visual Studio en particulier?
Et si la suppression peut changer l'adresse vers laquelle elle pointe de toute façon, pourquoi la suppression ne définirait-elle pas automatiquement le pointeur au NULL
lieu d'une adresse aléatoire?
Réponses:
J'ai remarqué que l'adresse stockée
ptr
était toujours remplacée par00008123
...Cela semblait étrange, alors j'ai fait un peu de fouille et j'ai trouvé ce billet de blog Microsoft contenant une section traitant de "Nettoyage automatisé des pointeurs lors de la suppression d'objets C ++".
Non seulement cela explique ce que Visual Studio fait avec le pointeur après sa suppression, mais cela explique également pourquoi ils ont choisi de NE PAS le définir
NULL
automatiquement!Cette "fonctionnalité" est activée dans le cadre du paramètre "Vérifications SDL". Pour l'activer / le désactiver, allez dans: PROJET -> Propriétés -> Propriétés de configuration -> C / C ++ -> Général -> Vérifications SDL
Pour confirmer ceci:
La modification de ce paramètre et la réexécution du même code produit la sortie suivante:
"feature" est entre guillemets car dans le cas où vous avez deux pointeurs vers le même emplacement, appeler delete ne nettoiera qu'UN d'entre eux. L'autre sera laissé pointant vers l'emplacement invalide ...
METTRE À JOUR:
Après 5 ans d'expérience en programmation C ++, je me rends compte que tout ce problème est fondamentalement un point discutable. Si vous êtes un programmeur C ++ et que vous utilisez toujours
new
et quedelete
vous gérez des pointeurs bruts au lieu d'utiliser des pointeurs intelligents (qui contournent tout ce problème), vous pouvez envisager un changement de cheminement de carrière pour devenir programmeur C. ;)la source
0x8123
place0
. Le pointeur n'est toujours pas valide, mais provoque une exception lors de la tentative de déréférencement (bon), et il ne passe pas les vérifications NULL (également bon, car c'est une erreur de ne pas le faire). Où est la place pour les mauvaises habitudes? C'est vraiment quelque chose qui vous aide à déboguer.Vous voyez les effets secondaires de l'
/sdl
option de compilation. Activé par défaut pour les projets VS2015, il permet des contrôles de sécurité supplémentaires au-delà de ceux fournis par / gs. Utilisez Projet> Propriétés> C / C ++> Général> SDL vérifie le paramètre pour le modifier.Citant l' article MSDN :
N'oubliez pas que la définition de pointeurs supprimés sur NULL est une mauvaise pratique lorsque vous utilisez MSVC. Il va à l'encontre de l'aide que vous obtenez à la fois du tas de débogage et de cette option / sdl, vous ne pouvez plus détecter les appels libres / supprimés non valides dans votre programme.
la source
delete
un, Visual Studio laissera le deuxième pointeur pointant vers son emplacement d'origine qui est désormais invalide.Ce sont certainement des informations trompeuses.
Ceci est clairement dans les spécifications linguistiques.
ptr
n'est pas valide après l'appel àdelete
. Utiliserptr
après qu'il ait étédelete
d est la cause d'un comportement indéfini. Ne fais pas ça. L'environnement d'exécution est libre de faire tout ce qu'il veutptr
après l'appel àdelete
.La modification de la valeur du pointeur vers une ancienne valeur est conforme à la spécification du langage. Pour ce qui est de le changer en NULL, je dirais que ce serait mauvais. Le programme se comporterait d'une manière plus saine si la valeur du pointeur était définie sur NULL. Cependant, cela cachera le problème. Lorsque le programme est compilé avec différents paramètres d'optimisation ou porté dans un environnement différent, le problème apparaîtra probablement au moment le plus inopportun.
la source
delete
deux fois sur le pointeur ne poserait pas de problème. Ce n'est certainement pas bon.NULL
mais aussi définitivement en dehors de l'espace d'adressage du processus exposera plus de cas que les deux alternatives. Le laisser en suspens ne provoquera pas nécessairement un segfault s'il est utilisé après avoir été libéré; le régler surNULL
ne provoquera pas de segfault s'il est àdelete
nouveau d.En général, même la lecture (comme vous le faites ci-dessus, notez: ceci est différent du déréférencement) des valeurs de pointeurs invalides (le pointeur devient invalide par exemple lorsque vous
delete
le faites ) est un comportement défini par l'implémentation. Cela a été introduit dans le CWG # 1438 . Voir aussi ici .Veuillez noter qu'avant cela, la lecture des valeurs de pointeurs non valides était un comportement non défini, donc ce que vous avez ci-dessus serait un comportement non défini, ce qui signifie que tout pourrait arriver.
la source
[basic.stc.dynamic.deallocation]
: "Si l'argument donné à une fonction de désallocation dans la bibliothèque standard est un pointeur qui n'est pas la valeur nulle du pointeur, la fonction de désallocation doit désallouer le stockage référencé par le pointeur, rendant invalides tous les pointeurs faisant référence à une partie du stockage désalloué »et la règle[conv.lval]
(section 4.1) qui dit lire (conversion lvalue-> rvalue) toute valeur de pointeur invalide est un comportement défini par l'implémentation.Je crois que vous exécutez une sorte de mode de débogage et VS tente de rediriger votre pointeur vers un emplacement connu, de sorte que toute autre tentative de déréférencement puisse être tracée et signalée. Essayez de compiler / d'exécuter le même programme en mode version.
Les pointeurs ne sont généralement pas modifiés à l'intérieur
delete
pour des raisons d'efficacité et pour éviter de donner une fausse idée de la sécurité. La définition du pointeur de suppression sur une valeur prédéfinie ne servira à rien dans la plupart des scénarios complexes, car le pointeur en cours de suppression ne sera probablement que l'un des nombreux pointant vers cet emplacement.En fait, plus j'y pense, plus je trouve que VS est en faute en le faisant, comme d'habitude. Que faire si le pointeur est const? Est-ce que ça va encore le changer?
la source
Après avoir supprimé le pointeur, la mémoire vers laquelle il pointe peut encore être valide. Pour manifester cette erreur, la valeur du pointeur est définie sur une valeur évidente. Cela aide vraiment le processus de débogage. Si la valeur était définie sur
NULL
, il se peut qu'elle ne s'affiche jamais comme un bogue potentiel dans le déroulement du programme. Cela peut donc masquer un bogue lorsque vous testez ultérieurementNULL
.Un autre point est que certains optimiseurs d'exécution peuvent vérifier cette valeur et modifier ses résultats.
Dans les temps anciens, MS définissait la valeur sur
0xcfffffff
.la source