web-dev-qa-db-fra.com

Que fait Visual Studio avec un pointeur supprimé et pourquoi?

Un livre C++ que j'ai lu indique que lorsqu'un pointeur est supprimé à l'aide de l'opérateur delete, 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 delete est appelé!

Pourquoi cela arrive-t-il? Cela a-t-il quelque chose à voir avec Visual Studio en particulier?

Et si supprimer peut changer l'adresse vers laquelle il pointe de toute façon, pourquoi ne supprimerait-il pas automatiquement le pointeur sur NULL au lieu d'une adresse aléatoire?

127
tjwrona1992

J'ai remarqué que l'adresse stockée dans ptr était toujours remplacée par 00008123...

Cela semblait étrange, alors j'ai fait un peu de recherche et j'ai trouvé cela article de blog Microsoft contenant une section sur "la désinfection automatisée du pointeur lors de la suppression d'objets C++".

... les vérifications de NULL sont une construction de code courante, ce qui signifie qu'une vérification existante de NULL combinée à l'utilisation de NULL comme valeur de nettoyage pourrait fortuitement masquer un véritable problème de sécurité de la mémoire dont la cause première doit vraiment être traitée.

Pour cette raison, nous avons choisi 0x8123 comme valeur de nettoyage - du point de vue du système d'exploitation, c'est dans la même page de mémoire que l'adresse zéro (NULL), mais une violation d'accès à 0x8123 ressortira mieux au développeur comme nécessitant une attention plus détaillée. .

Non seulement il explique ce que Visual Studio fait avec le pointeur après sa suppression, mais il explique également pourquoi ils ont choisi de ne PAS le définir automatiquement sur NULL!


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 le confirmer:

La modification de ce paramètre et la réexécution du même code produisent la sortie suivante:

ptr = 007CBC10
ptr = 007CBC10

"feature" est entre guillemets car dans le cas où vous avez deux pointeurs vers le même emplacement, l'appel de delete ne désinfectera que [~ # ~] un [~ # ~] d'entre eux. L'autre pointera vers l'emplacement non valide.

Visual Studio pourrait vous mettre en place pour une situation délicate en omettant de documenter cette faille dans sa conception.

172
tjwrona1992

Vous voyez les effets secondaires du /sdl option de compilation. Activé par défaut pour les projets VS2015, il permet des vérifications de sécurité supplémentaires au-delà de celles fournies par/gs. Utilisez Projet> Propriétés> C/C++> Général> SDL vérifie le paramètre pour le modifier.

Citant de article MSDN :

  • Effectue une désinfection limitée du pointeur. Dans les expressions qui n'impliquent pas de déréférences et dans les types qui n'ont pas de destructeur défini par l'utilisateur, les références de pointeur sont définies sur une adresse non valide après un appel à supprimer. Cela permet d'éviter la réutilisation des références de pointeur périmées.

N'oubliez pas que la définition de pointeurs supprimés sur NULL est une mauvaise pratique lorsque vous utilisez MSVC. Il bat 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 gratuits/supprimés invalides dans votre programme.

30
Hans Passant

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.

Ce sont certainement des informations trompeuses.

Il est clair que l'adresse vers laquelle pointe le pointeur change lorsque delete est appelé!

Pourquoi cela arrive-t-il? Cela a-t-il quelque chose à voir avec Visual Studio en particulier?

Ceci est clairement dans les spécifications linguistiques. ptr n'est pas valide après l'appel à delete. L'utilisation de ptr après qu'il a été deleted est à l'origine d'un comportement non défini. Ne le faites pas. L'environnement d'exécution est libre de faire ce qu'il veut avec ptr après l'appel à delete.

Et si supprimer peut changer l'adresse vers laquelle il pointe de toute façon, pourquoi ne supprimerait-il pas automatiquement le pointeur sur NULL au lieu d'une adresse aléatoire ???

La modification de la valeur du pointeur sur une ancienne valeur est conforme aux spécifications de la langue. En ce qui concerne la modification de 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 masquera le problème. Lorsque le programme est compilé avec différents paramètres d'optimisation ou porté vers un environnement différent, le problème apparaîtra probablement au moment le plus inopportun.

19
R Sahu
delete ptr;
cout << "ptr = " << ptr << endl;

En général, même lecture (comme vous le faites ci-dessus, notez: c'est différent du déréférencement) des valeurs de pointeurs invalides (le pointeur devient invalide par exemple lorsque vous delete it) est un comportement défini par l'implémentation. Cela a été introduit dans CWG # 1438 . Voir aussi ici .

Veuillez noter qu'avant cette lecture, les valeurs des pointeurs invalides étaient un comportement indéfini, donc ce que vous avez ci-dessus serait un comportement indéfini, ce qui signifie que tout pourrait arriver.

10
giorgi moniava

Je crois que vous utilisez une sorte de mode de débogage et VS tente de rediriger votre pointeur vers un emplacement connu, de sorte que d'autres tentatives de déréférencement puissent être tracées et signalées. Essayez de compiler/exécuter le même programme en mode release.

Les pointeurs ne sont généralement pas modifiés à l'intérieur de delete pour des raisons d'efficacité et pour éviter de donner une fausse idée de la sécurité. Définir le pointeur de suppression sur une valeur prédéfinie ne sera d'aucune utilité dans la plupart des scénarios complexes, car le pointeur en cours de suppression ne sera probablement qu'un parmi plusieurs pointant vers cet emplacement.

En fait, plus j'y pense, plus je trouve que VS est en faute en le faisant, comme d'habitude. Et si le pointeur est const? Est-ce que ça va encore le changer?

1
SergeyA

Après avoir supprimé le pointeur, la mémoire vers laquelle il pointe peut toujours ê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 a été définie sur NULL, elle peut ne jamais apparaître comme bogue potentiel dans le flux du programme. Il peut donc masquer un bogue lorsque vous testez plus tard contre NULL.

Un autre point est que certains optimiseurs d'exécution peuvent vérifier cette valeur et modifier ses résultats.

Auparavant, MS définissait la valeur sur 0xcfffffff.

0
Karsten