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  using namespace std; int main() { int* ptr = new int; cout << "ptr = " << ptr << endl; delete ptr; cout << "ptr = " << ptr << endl; system("pause"); return 0; } 

Quand je comstack et exécute ce programme, j’obtiens la sortie suivante:

 ptr = 0050BC10 ptr = 00008123 Press any key to continue.... 

Clairement l’adresse que le pointeur pointe sur les changements 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 delete peut changer l’adresse vers laquelle il pointe de toute façon, pourquoi la suppression ne définit-elle pas automatiquement le pointeur sur NULL au lieu d’une adresse aléatoire?

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é ce billet de blog de Microsoft contenant une section traitant de “la désinfection automatique du pointeur lors de la suppression d’objects C ++”.

Les vérifications de NULL sont une construction de code commune, ce qui signifie qu’une vérification de NULL associée à l’utilisation de la valeur NULL comme valeur de désinfection pourrait masquer un véritable problème de sécurité de la mémoire dont la cause principale nécessite un adressage.

Pour cette raison, nous avons choisi 0x8123 comme valeur de désinfection – du sharepoint vue du système d’exploitation, cela se trouve dans la même page de mémoire que l’adresse zéro (NULL), mais une violation d’access à 0x8123 se distinguera .

Non seulement il explique ce que fait Visual Studio 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 / désactiver, accédez à: PROJET -> Propriétés -> Propriétés de configuration -> C / C ++ -> Général -> Vérifications SDL

Pour confirmer ceci:

Modifier ce paramètre et réexécuter le même code produit 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 à delete ne va assainir que l’ un d’entre eux. L’autre sera laissé pointant vers l’emplacement invalide.

Visual Studio pourrait vous préparer à une situation délicate en ne documentant pas cette faille dans sa conception.

Vous voyez les effets secondaires de l’option de compilation /sdl . Activée par défaut pour les projets VS2015, elle active des contrôles de sécurité supplémentaires par rapport à 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 :

  • Effectue une désinfection limitée du pointeur. Dans les expressions n’impliquant pas de déréférence et dans les types sans 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 de références de pointeur obsolètes.

N’oubliez pas que la définition de pointeurs supprimés sur NULL est une mauvaise pratique lorsque vous utilisez MSVC. Cela va à l’encontre de l’aide que vous obtenez à la fois de Debug Heap et de cette option / sdl, vous ne pouvez plus détecter les appels gratuits / supprimés non valides dans votre programme.

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.

C’est certainement une information trompeuse.

Clairement l’adresse que le pointeur pointe sur les changements lorsque la suppression est appelée!

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 sa delete d provoque un comportement indéfini. Ne le fais 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 pas supprimer automatiquement définir le pointeur sur NULL au lieu d’une adresse aléatoire ???

La modification de la valeur du pointeur sur une ancienne valeur est comprise dans la spécification de langue. En ce qui concerne le changement en NULL, je dirais que ce serait mauvais. Le programme se comporterait de 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 parameters d’optimisation ou porté dans un environnement différent, le problème apparaîtra probablement au moment le plus inopportun.

 delete ptr; cout << "ptr = " << ptr << endl; 

En général, même la lecture (comme vous le faites ci-dessus, notez que ceci est différent du déréférencement) des valeurs de pointeurs non valides (le pointeur devient invalide par exemple lorsque vous le delete ) 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 indéfini, donc ce que vous avez ci-dessus serait un comportement indéfini, ce qui signifie que tout peut arriver.

Je crois que vous exécutez une sorte de mode de débogage et que VS tente de repositionner votre pointeur sur un emplacement connu, de sorte que d’autres tentatives de déréférencement pourraient être effectuées. Essayez de comstackr / exécuter le même programme en mode de libération.

Les pointeurs ne sont généralement pas modifiés à l’intérieur de la delete pour des raisons d’efficacité et pour éviter de donner une fausse idée de la sécurité. Le fait de définir le pointeur de suppression sur une valeur prédéfinie ne fonctionnera pas dans la plupart des scénarios complexes, étant donné que le pointeur en cours de suppression est probablement le seul pointage vers cet emplacement.

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

Après avoir supprimé le pointeur, la mémoire sur 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 était définie sur NULL , elle pourrait ne jamais apparaître comme un bogue potentiel dans le stream du programme. Donc, cela peut cacher un bug 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 .