Указатель загадочным образом сбрасывается в NULL - PullRequest
1 голос
/ 02 марта 2009

Я работаю над игрой, и в настоящее время я работаю над частью, которая обрабатывает ввод. Здесь задействованы три класса, есть класс ProjectInstance, который запускает уровень и прочее, есть GameController, который будет обрабатывать ввод, и PlayerEntity, на который будут влиять элементы управления, определенные GameController. После запуска уровня ProjectInstance создает GameController и вызывает метод EvaluateControls в методе Step, который вызывается внутри игрового цикла. Метод EvaluateControls выглядит примерно так:

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
}

Эта функция вызывается правильно, и утверждение никогда не срабатывает. Однако каждый раз, когда вызывается эта функция, gc_ppePlayer устанавливается в NULL. Как видите, это не локальная переменная, выходящая из области видимости. Единственное место gc_ppePlayer, которое может быть установлено в NULL, находится в конструкторе или, возможно, в деструкторе, ни одно из которых не вызывается между вызовами EvaluateControls. При отладке gc_ppePlayer получает правильное и ожидаемое значение до возврата. Когда я нажимаю F10 еще раз и курсор находится на закрывающей скобке, значение меняется на 0xffffffff. Я в недоумении, как это может случиться? Кто-нибудь?

Ответы [ 5 ]

5 голосов
/ 02 марта 2009

устанавливает точку наблюдения на gc_ppePlayer == NULL, когда значение этого выражения изменяется (на NULL или от NULL), отладчик укажет вам точно, где это произошло.

Попробуйте и посмотрите, что получится. Ищите незавершенные строки или быстрое копирование в память, которая слишком мала и т. Д., Обычно это является причиной проблемы случайной перезаписи глобальных / стековых переменных.

Чтобы добавить точку наблюдения в VS2005 (инструкция от brone)

  1. Перейти в окно точек останова
  2. Нажмите Новый,
  3. Нажмите Точка останова данных. Введите
  4. &gc_ppePlayer в поле адреса, оставьте другие ценности одни.
  5. Тогда беги.

Когда gc_ppePlayer изменяется, точка остановки будет поражен. - броня

2 голосов
/ 02 марта 2009

Моя первая мысль - сказать, что SpawnEntity () возвращает указатель на внутренний член, который очищается при вызове memDelete (). Мне не ясно, когда указатель установлен в 0xffffffff, но если это происходит во время вызова memDelete (), то это объясняет, почему ваш ASSERT не запускается - 0xffffffff не совпадает с NULL.

Сколько времени прошло с тех пор, как вы перестроили всю кодовую базу? Время от времени я сталкиваюсь с такими проблемами с памятью, которые устраняются простым перестроением всего решения.

Вы пытались сделать шаг в (F11) вместо шага через (F10) в конце функции? Хотя в вашем примере не показаны локальные переменные, возможно, вы упустили некоторые из них для простоты. Если это так, F11 (надеюсь) перейдет к деструкторам для любой из этих переменных, что позволит вам увидеть, не является ли одна из них причиной проблемы.

2 голосов
/ 02 марта 2009

Вы отлаживаете конфигурацию выпуска или отладки? В конфигурации сборки выпуска то, что вы видите в отладчике, не всегда верно. Оптимизация сделана, и это может заставить окно наблюдения показывать причудливые значения, которые вы видите.

Вы действительно видите срабатывание ASSERT? ASSERT обычно компилируются из сборок Release, поэтому я предполагаю, что вы отлаживаете сборку релиза, поэтому ASSERT не вызывает завершение приложения.

Я бы порекомендовал собрать отладочную версию программного обеспечения, а затем посмотреть, действительно ли gc_ppePlayer равен NULL. Если это действительно так, возможно, вы видите какое-то повреждение кучи памяти, когда этот указатель переопределяется. Но если бы это было повреждение памяти, это было бы гораздо менее детерминированным, чем вы описываете.

Кроме того, использование таких глобальных значений указателей, как правило, считается плохой практикой. Посмотрите, можете ли вы заменить его одноэлементным классом, если он действительно является единым объектом и должен быть глобально доступен.

0 голосов
/ 02 марта 2009

В зависимости от того, на какой платформе вы находитесь, существуют инструменты (бесплатные или платные), которые могут быстро определить проблему с памятью такого типа.

С макушки головы:

0 голосов
/ 02 марта 2009

У вас "ядро фанданго".

Динамическая инициализация перезаписывает различные биты (sic) памяти.

Либо прямо, либо косвенно глобальный объект перезаписывается. где находится глобал в памяти относительно кучи?

бинарная нарезка динамически инициализированной части, пока проблема не исчезнет. (закомментируйте половину за раз, рекурсивно)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...