Я использую Code :: Blocks с Mingw уже много лет, со встроенным отладчиком (gdb) у меня возникают проблемы с указателем только тогда, когда у меня включена оптимизация, в противном случае он всегда знает этот указатель и может разыменовать его в любое время ,

нальный вопрос:

Почему указатель this равен 0 в сборке выпуска VS c ++?

При разрыве сборки выпуска Visual Studio 2008 с пакетом обновления 1 (SP1) с параметрами / Zi (компилятор: формат информации отладки - база данных программы) и / DEBUG (компоновщик: генерировать информацию об отладке, да), почему указатели «this» всегда 0x00000000?

РЕДАКТИРОВАТЬ: Перефразированный вопрос:

Мой оригинальный вопрос был довольно неясен, извините за это. При использовании отладчика Visual Studio 2008 для пошагового выполнения программы я вижу все переменные, кроме переменных-членов локального объекта. Вероятно, это связано с тем, что отладчик выводит их из указателя this, но VS всегда говорит, что это 0x00000000, поэтому он не может получить переменные-члены текущего объекта (он не знает позицию объекта в памяти)

При загрузке мегапамяти (подобно мини-дампу Windows, но содержащему все пространство памяти процесса) я могу просматривать все свои локальные переменные (определенные в функции) и целые древовидные структуры в куче, даже если у меня есть указатели.

Например: при взломе A :: foo () в режиме Release

'this' будет иметь значение 0x00000000
'f_' покажет мусор

Каким-то образом эта информация должна быть доступна для процесса. Это отсутствующая функция в VS2008? Любой другой отладчик, который обрабатывает это правильно?

class A
{
  void foo() { /*break here*/ }
  int f_;
};
 Pieter09 мар. 2011 г., 12:25
Есть обходной путь. Когда я возвращаюсь на один или несколько шагов назад в стек вызовов, я могу найти, в зависимости от кода, указатель объекта, для которого я вызываю функцию.
 Bo Persson09 мар. 2011 г., 18:59
Это также может зависеть от того, что делает foo (). Если оптимизатор решит, что «this» больше не нужен, он может повторно использовать регистр.
 harper11 янв. 2011 г., 12:49
Как проверить, что указатель this равен NULL? Отладчик не всегда показывает правильное значение указателя this в сборке выпуска.

Ответы на вопрос(8)

foo являетсяinline (он объявлен в определении класса, поэтому неявноinline) и не имеет доступа ни к каким участникам. Поэтому оптимизатор, скорее всего, фактически не пройдетthis указатель вообще, когда он компилирует код, поэтому он не доступен для отладчика.

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

Иногда отладочной информации достаточно, чтобы отладчик мог собратьthis указатель и значения локальных переменных. Часто это не так, иthis указатель, показанный в окне наблюдения (и, следовательно, переменные-члены), является бессмысленным.

вы должны убедиться, что компилятор не делает ничего, что может сбить с толку отладчик, оптимизация может сделать. Тот факт, что у вас естьNULL указатель может произойти, если вы вызываете функцию статически, например:

A* b=NULL;
b->foo();

Функция здесь не статична, а называется статическим способом.

Лучшее место, чтобы найти реальноеthis указатель является взглядом на стек. Для нестатических функций классаthis указатель ДОЛЖЕН быть первым (скрытым) аргументом вашей функции.

class A
{
  void foo() { } // this is "void foo(A *this)" really
  int f_;
};

Если твойthis В этом случае prointer имеет значение null, поэтому перед вызовом функции возникают проблемы. Если указатель здесь верен, то ваш отладчик как-то запутался.

Я использую Code :: Blocks с Mingw уже много лет, со встроенным отладчиком (gdb) у меня возникают проблемы с указателем только тогда, когда у меня включена оптимизация, в противном случае он всегда знает этот указатель и может разыменовать его в любое время ,

нулевому указателю.

Изменить: Перечитайте ваш вопрос. Скорее всего, это связано с тем, что оптимизатор сделал число в вашем коде, а отладчик больше не может его читать. Если у вас есть проблема, специфичная для сборки выпуска, то это подсказка, что в вашем коде есть хитрый #ifdef, или вы вызвали UB, который просто работает в режиме отладки. Иначе, отладка с помощью Debug build. Тем не менее, это неужасно полезно, если у вас действительно есть проблема в режиме выпуска, которую вы не можете найти.

 Steve Townsend11 янв. 2011 г., 13:25
Учитывая характеристику (затронуты все функции), это маловероятно.
 Puppy11 янв. 2011 г., 14:00
Ах, ты прав. Я думаю, что неправильно понял вопрос.
Решение Вопроса

компиляция в режиме Release делает определенные оптимизации (особенно исключающие использование ebp / rbp в качестве указателя кадра), которые нарушают предположения, на которые опирается отладчик для определения ваших локальных переменных. Однако знание того, почему это происходит, не очень полезно для отладки вашей программы!

Вот способ, которым вы можете обойти это: в самом начале вызова метода (разрыв первой строки функции, а не открывающей скобки),this указатель всегда будет найден в определенном регистре (ecx в 32-битных системах или rcx в 64-битных системах). Отладчик знает это, и поэтому вы должны увидеть значениеthis прямо в начале вашего вызова метода. Затем вы можете скопировать адрес из столбца Значение и посмотреть его конкретно (как(MyObject *)0x003f00f0 или что угодно), что позволит вам увидеть вthis позже в методе.

Если это не достаточно хорошо (например, потому что вы хотите останавливаться только при появлении ошибки, что составляет очень небольшой процент времени вызова данного метода), вы можете попробовать этот более продвинутый (и менее надежный) прием , Обычноthis указатель извлекается из ecx / rcx очень рано при вызове функции, потому что это регистр «спасения вызывающего абонента», что означает, что его значение может быть забито и не восстановлено вызовами функций, которые создает ваш метод (это также необходимо для некоторых инструкций, которые Можнотолько используйте этот регистр для своего операнда, например REP * и некоторые из команд сдвига). Однако, если ваш метод используетthis много указатель (включая неявное использование ссылки на переменные-члены или вызов виртуальных функций-членов), компилятор, вероятно, сохранилthis в другом регистре - регистр «сохраняемый» (это означает, что любая функция, которая его забивает, должна восстановить ее перед возвратом).

Практическим результатом этого является то, что в вашем окне просмотра вы можете попробовать посмотреть на(MyObject *) ebp, (MyObject *) esiи так далее с другими регистрами, пока вы не обнаружите, что смотрите на указатель, который, вероятно, является правильным (потому что переменные-члены совпадают с вашим ожиданием содержимогоthis во время вашей точки останова). На x86 регистры, сохраняемые при вызове, - это ebp, esi, edi и ebx. На x86-64 это rbp, rsi, rdi, rbx, r12, r13, r14 и r15. Если вы не хотите искать все это, вы всегда можете попробовать разобрать пролог функции, чтобы увидеть, во что копируется ecx (или rcx).

 Pieter22 апр. 2011 г., 14:19
Хороший ответ, TNX! Я попытался привести разные значения реестра, но не нашел читаемой информации.
 xjdrew16 мар. 2011 г., 03:47
Вы можете отключить оптимизацию компилятора с помощью параметра / Od, тогда все будет хорошо.

чтобы изменить детали реализации программы, сохранив при этом общую функциональность.

Программа все еще работает? Тогда это недело чтоthis указатель, по-видимому, нулевой.

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

Когда оптимизация включена, никакие гарантии не предоставляются ни по одной из этих вещей. Но компилятор не будетсломать ваша программа. Если он работал без оптимизации, он все равно будет работатьс участием оптимизаций. Если это вдруг не работает, это потому, что у вас есть ошибка, которая была обнаружена только потому, что компилятор оптимизировал и изменил код.

this) при просмотре в окне Locals нельзя полагаться на сборку Release так, как это можно делать в сборках Debug. Правильно ли показано значение переменной в любой данной инструкции, зависит от того, как используется базовый регистр в этой точке. Если в Debug код работает нормально, маловероятно, что значение на самом деле равно 0.

Оптимизация в сборках Release делает невооруженным глазом значения в окне Locals просто дерьмом. Без одновременного отображения и сопоставления окна «Разборка» вы не можете быть уверены, что окно «Местные жители» сообщает вам фактическое значение переменной. Если вы пошагово проходите через код (возможно, в разделе «Разборка, а не исходный код») к строке, которая фактически используетthis, более вероятно, что вы увидите действительное значение там.

Функция const - это функция, которая объявлена ​​с ключевым словом const, и это означает, что она не изменит ни одного члена, а только прочитает их (например, функции доступа)

Оптимизирующий компилятор может не потрудиться передать указатель this в некоторые константные функции, если он даже не читает из нестатических переменных-членов

Оптимизирующий компилятор может искать функции, которые могут быть константными, делать их постоянными, а затем не передавать в них указатель this, в результате чего отладчик не сможет найти ловушку.

Это неthis указатель, который является НЕДЕЙСТВИТЕЛЬНЫМ, но скорее указатель, который вы используете для вызова функции-члена:

class A
{
public:
    void f() {}
};

int main()
{
    A* a = NULL;
    a->f(); // DO'H!  NULL pointer access ...

    // FIX
    A* a = new A;
    a->f(); // Aha!
}
 Puppy11 янв. 2011 г., 13:24
Не обязательно, так как исходный a не инициализирован в NULL, он может содержать какой-либо старый мусор, и C ++ не определяет, что будет исключение - фактически, вы получите только Структурированное Исключение в Windows, на других платформах, которые являются Unix на основе получит сигнал.
 CrazyC11 янв. 2011 г., 14:09
Если его класс A, то даже с указателем NULL он не будет разбит. Отметьте это как ((класс *) (NULL)) -> f (); Это будет сбой при доступе к любой переменной-члену в функции f. В любом случае, неинициализируемые местные жители всегда имеют мусор, а поведение непредсказуемо.
 Zac Howland11 янв. 2011 г., 13:55
Ты прав. Я исправил формулировку.

Ваш ответ на вопрос