David O'Neil Ответов: 1

Какие-нибудь рекомендуемые методы отладки мне не хватает?


У меня есть глобальный объект, в котором есть член-указатель. Программная функция вызывает глобальный указатель и инициализирует его. Затем позже осуществляется доступ к указателю. По какой-то причине в режиме отладки указатель читает "NULL" вместо адреса объекта. newЭд пойнтер.

При использовании непосредственного окна Visual Studio адрес указателя одинаков в обоих местах, поэтому у меня нет конфликта имен переменных. Указатель находится в библиотеке, которая является проектом решения, и объявлен там глобальным, а в главном приложении - "внешним". При пошаговом переходе часы наведения мыши Visual Studio показывают элемент указателя, содержащий число (ячейку памяти), но при назначении его локальной переменной эта локальная переменная становится нулевой.

Как ни странно, программа, похоже, работает в режиме выпуска. Тот факт, что отладочная версия является flakey, вызывает беспокойство.

У кого-нибудь есть какие-нибудь идеи, чтобы помочь отладить это дальше?

Спасибо!

Что я уже пробовал:

Я попытался переместить инициализацию члена в строку прямо перед доступом:
gDwlGlobals->setupItemsNeedingHwnd(); //Initialize 'dialogs'
void * address = &gDwlGlobals->dialogs; //For debugging, to see address
dwl::WinDialogs * d = gDwlGlobals->dialogs; //Here it is NULL, even though I just
                                            //stepped through and watched it initialize
d = gDialogs; //If, when I initialize it in 'setupItems...', I create another global
              //pointer and assign the memory address to it, and use that instead
              //of the main gDwlGlobal->dialogs, it will show up correctly here!

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

Еще одна подсказка заключается в том, что действие "наведения" в отладчике, где я беру адрес указателя как void*, показывает, что адрес находится на расстоянии 20 байт от значения, заданного непосредственным окном. Что может быть причиной этого?

1 Ответов

Рейтинг:
1

Jochen Arndt

На этот вопрос нельзя ответить, не зная типа объекта. gDwlGlobals переменная, как она инициализируется и что именно setupItemsNeedingHwnd() делает (то, что назначено члену диалогов).

Но

void * address = &gDwlGlobals->dialogs;
получает адрес объекта dialogs член (адрес gDwlGlobals плюс смещение), а
dwl::WinDialogs * d = gDwlGlobals->dialogs;
получает значение параметра dialogs член (который также является указателем).

Эти ценности никогда не будут одинаковыми. Это также объясняет, почему у вас есть смещение в 20 байт между address и gDwlGlobals показано в непосредственном окне.

Одно из различий между отладочными и выпускными сборками заключается в том, что переменные инициализируются нулем / NULL в отладочных сборках они содержат случайные значения, а в выпускных сборках-случайные значения. Это не относится к глобальным (статическим) переменным, которые всегда инициализируются нулем / NULL... Но ваш gDwlGlobals является указателем так, что только указатель инициализируется с помощью NULL.

Убедитесь также, что ваша программа выполнялась до определенного момента, прежде чем проверять переменные в непосредственном окне. Если вы установите, например, точку останова на setupItemsNeedingHwnd() линия, которая не будет выполнена. Чтобы увидеть эффекты этой функции, вы должны установить точку останова на следующей строке.


David O'Neil

Спасибо за ответ, Йохен. Я немного поспал, а затем просто включил протоколирование звонков на каждом шагу. И после того, как они были установлены, он начал работать так, как я думал, что это должно быть. Поэтому я еще немного поиграл с лесозаготовками.

В конце концов, меня осенило. В библиотечном проекте было определено DWL_DO_LOGGING, а в основном приложении-нет. Итак, дополнительный "регистратор" в глобальной библиотеке сбрасывал адрес! Ну и ну!!! Вернемся к моему универсальному заголовку!

Имейте "5" за ваше время! Спасибо!

Jochen Arndt

Спасибо Вам за ваши отзывы и прекрасно слышать, что все решается.

Rick York

The moral of the story is avoid adding data members based on macro definitions especially when those definitions are outside of the data declaration. I have been bitten by this only a few times but the last one cost about two days and I have never done it again. In that case, I was using a library built in release mode with an app built in debug mode. The FILE structure had an extra member in debug mode and when I passed a FILE pointer into the library it would crash. This was a fairly obscure case but I have avoided doing anything like that since then. This was not something in code actually, but the situation was definitely a learning experience on several topics.

David O'Neil

Да, это стоило мне целого дня. Урок усвоен? Я добавил еще одну конфигурацию к решению, в котором включены функции "отладка" и "ведение журнала". У меня было немного разочарования, потому что, когда я добавил его, обе конфигурации отладки и выпуска переключились на 64-битную версию! И не мог быстро понять, как это исправить - думал, что мне придется воссоздать все решение! Но я все понял.