Pointeur revenant mystérieusement à NULL
Question
Je travaille sur un jeu et je travaille actuellement sur la partie qui gère les entrées. Trois classes sont impliquées ici: il y a la classe ProjectInstance
qui commence le niveau et le reste, il y a un GameController
qui gérera l'entrée et un PlayerEntity
qui sera influencé par les contrôles déterminés par GameController
. Au démarrage du niveau, ProjectInstance crée le GameController
et appelle sa méthode EvaluateControls
dans la méthode Step, qui est appelée à l'intérieur de la boucle de jeu. La méthode EvaluateControls
ressemble un peu à ceci:
void CGameController::EvaluateControls(CInputBindings *pib) {
// if no player yet
if (gc_ppePlayer == NULL) {
// create it
Handle<CPlayerEntityProperties> hep = memNew(CPlayerEntityProperties);
gc_ppePlayer = (CPlayerEntity *)hep->SpawnEntity();
memDelete((CPlayerEntityProperties *)hep);
ASSERT(gc_ppePlayer != NULL);
return;
}
// handles controls here
}
Cette fonction est appelée correctement et l'assert ne se déclenche jamais. Cependant, chaque fois que cette fonction est appelée, gc_ppePlayer
est défini sur NULL. Comme vous pouvez le constater, ce n'est pas une variable locale qui sort de la portée. Le seul endroit gc_ppePlayer
pouvant être défini sur NULL est dans le constructeur ou éventuellement dans le destructeur, aucun des deux n'étant appelé entre les appels à EvaluateControls
. Lors du débogage, gc_ppePlayer
reçoit une valeur correcte et attendue avant le retour. Lorsque j'appuie sur F10 une fois de plus et que le curseur est sur l'accolade fermante, la valeur devient 0xffffffff. Je suis perdu ici, comment cela peut-il arriver? Quelqu'un?
La solution
Déboguez-vous une configuration Release ou Debug? Dans la configuration de la version finale, ce que vous voyez dans le débogueur n'est pas toujours vrai. Des optimisations sont apportées. La fenêtre de surveillance peut ainsi afficher des valeurs bizarres, comme vous le voyez.
Voyez-vous réellement le déclenchement de l’ASSERT? Les ASSERT sont normalement compilés à partir de versions Release, alors je suppose que vous êtes en train de déboguer une version Release. C’est pourquoi ASSERT ne provoque pas la fermeture de l’application.
Je recommanderais de créer une version Debug du logiciel, puis de vérifier si gc_ppePlayer est vraiment NULL. Si c'est vraiment le cas, vous constatez peut-être une corruption de mémoire dans laquelle le pointeur est remplacé. Mais si c’était une corruption de mémoire, ce serait généralement beaucoup moins déterministe que ce que vous décrivez.
En passant, l’utilisation de valeurs de pointeur globales comme celle-ci est généralement considérée comme une mauvaise pratique. Voyez si vous pouvez remplacer ceci par une classe singleton s’il s’agit vraiment d’un objet unique et doit être accessible de manière globale.
Autres conseils
Définissez un point d'observation sur gc_ppePlayer == NULL
lorsque la valeur de cette expression change (en NULL ou à partir de NULL), le débogueur vous indiquera exactement où il s'est passé.
Essayez cela et voyez ce qui se passe. Recherchez des chaînes non terminées ou des copies mempcy en mémoire trop petites, etc., ce qui est généralement la cause du problème de l'écrasement aléatoire des variables de pile / globales.
Pour ajouter un point de contrôle dans VS2005 (instructions détaillées)
- Accéder à la fenêtre Points d'arrêt
- Cliquez sur Nouveau,
- Cliquez sur le point d'arrêt de données. Entrez
& amp; gc_ppePlayer
dans la zone Adresse, laissez autres valeurs seules.- Puis courez.
Lorsque
gc_ppePlayer
change, point d'arrêt sera touché. - brone
Ma première pensée est de dire que SpawnEntity () renvoie un pointeur sur un membre interne qui est "effacé". quand memDelete () est appelé. Ce n'est pas clair pour moi quand le pointeur est réglé sur 0xffffffff, mais s'il se produit pendant l'appel à memDelete (), cela explique pourquoi votre ASSERT ne se déclenche pas - 0xffffffff n'est pas identique à NULL.
Depuis combien de temps avez-vous reconstruit la base de code entière? J'ai vu de temps en temps des problèmes de mémoire comme celui-ci, qui sont résolus en reconstruisant simplement la solution complète.
Avez-vous essayé de faire un pas dans (F11) au lieu du pas (F10) à la fin de la fonction? Bien que votre exemple ne présente aucune variable locale, vous en avez peut-être oublié pour des raisons de simplicité. Si tel est le cas, F11 entrera (espérons-le) dans les destructeurs de l'une de ces variables, vous permettant de voir si l'un d'entre eux est à l'origine du problème.
Vous avez un "fandango sur le noyau".
L’initialisation dynamique écrase divers bits de mémoire.
Que ce soit directement ou indirectement, le fichier global est écrasé. où est le global en mémoire par rapport au tas?
binaire coupe la partie initialisée dynamiquement jusqu'à ce que le problème disparaisse. (commentez récursivement, moitié à la fois)
Selon la plate-forme sur laquelle vous vous trouvez, il existe des outils (gratuits ou payants) qui peuvent rapidement résoudre ce type de problème de mémoire.
De mémoire: