Неинициализированные блоки памяти в VC++

Как всем известно, среда выполнения Visual C++ отмечает неинициализированные или только что освобожденные блоки памяти специальными ненулевыми маркерами. Есть ли способ полностью отключить это поведение, не устанавливая вручную всю неинициализированную память на нули? Это вызывает хаос с моими действительными ненулевыми проверками, начиная с 0xFEEEFEEE != 0.

Хм, возможно, мне стоит объяснить немного лучше. Я создаю и инициализирую переменную (через new), и все идет нормально. Когда я освобождаю его (с помощью удаления), он устанавливает указатель на 0xFEEEFEEE вместо NULL. Когда я вставляю правильную проверку на NULL, как и все хорошие программы, которые управляют своей собственной памятью, у меня возникают проблемы, поскольку 0xFEEEFEEE проходит проверку NULL без проблем. Есть ли какой-либо хороший способ, кроме ручной установки всех указателей на NULL при их удалении, чтобы определить, когда память уже освобождена? Я бы предпочел не использовать Способствовать росту просто потому, что мне не нужны накладные расходы, хотя они могут быть небольшими, поскольку это единственное, для чего я бы использовал Boost.

Умм, delete все равно не устанавливает указатели в NULL. В вашей программе есть ошибка, если вы предполагаете, что она есть

1800 INFORMATION 24.01.2009 11:34

действительно, почти все удаления должны сопровождаться строкой, которая явно устанавливает указатель на NULL. А еще лучше почаще используйте умные указатели.

Evan Teran 31.03.2009 21:35

Вы сказали: «Когда я освобождаю его (с помощью удаления), он устанавливает указатель на 0xFEEEFEEE». Вы можете это прояснить? Может вы имеете в виду, что для памяти указал на установлено значение 0xFEEEFEEE? Или вы действительно имеете в виду, что сам указатель настроен на адрес 0xFEEEFEEE?

Aaron McDaid 09.02.2012 00:40
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
3
10 757
15
Перейти к ответу Данный вопрос помечен как решенный

Ответы 15

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

Вы можете избавить себя от множества головных болей, используя класс интеллектуальных указателей (например, boost::shared_ptr), который автоматически определяет, инициализирован ли указатель или нет.

Полностью согласен. Всегда следует выполнять инициализацию новой памяти до нуля. В качестве стороны, я заметил одну вещь - ненужную проверку ptr! = NULL перед удалением. Всегда безопасно удалить указатель, имеющий значение NULL. Не в случае неинициализированного ptr.

Scott Saad 15.09.2008 23:49

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

Лучше всего просто привыкнуть ставить их на 0 в первую очередь, это всего лишь 2 дополнительных символа.

int *ptr=0;

Вы также можете использовать макрос NULL, который определяется как 0 (но не по умолчанию, поэтому будьте осторожны с несколькими определениями при включении таких вещей, как windows.h, и определении его самостоятельно!

Поведение VC++ не должно вызывать хаоса при любой проверке действительный, которую вы можете выполнить. Если вы видите 0xfeeefeee, значит, вы не записали в память (или освободили ее), поэтому вам все равно не следует читать из памяти.

Если вы выполняете сборку в режиме выпуска вместо режима отладки, среда выполнения вообще не заполняет неинициализированную память, но все равно не будет нулей. Однако вы должны зависеть от этого поведения нет - вы должны либо явно инициализировать память самостоятельно с помощью memset (), ZeroMemory () или SecureZeroMemory (), либо установить где-нибудь флаг, указывающий, что память еще не инициализирована. Чтение неинициализированной памяти приведет к неопределенному поведению.

Если вы читаете неинициализированную память, ваши чеки наверняка недействительны. Память освобождена. Возможно, он уже используется для чего-то другого. Вы не можете делать никаких предположений относительно содержимого неинициализированной памяти в C / C++.

Java (и, как я полагаю, C#) гарантирует, что выделенная память обнуляется перед использованием, и, конечно же, сборка мусора не позволяет вам вообще видеть освобожденную память. Но это не свойство кучи C, которая напрямую предоставляет доступ к памяти.

Ответ принят как подходящий

delete не несет ответственности за сброс всех указателей на объект на NULL. Также вы не должны изменять заполнение памяти по умолчанию для среды выполнения Windows DEBUG, и вы должны использовать что-то вроде boost::shared_ptr<> для указателей в любом случае.

Тем не менее, если вы действительно хотите выстрелить себе в ногу, вы можете.

Вы можете изменятьзаливка по умолчанию для окон Среда выполнения DEBUG, используя такой хук распределителя. Это будет работать только с выделенным объектом HEAP!

int main(int argc,char** arv)
{
  // Call first to register hook    
  _CrtSetAllocHook(&zero_fill);
  // Do other stuff
  malloc(100);
}


int zero_fill(int nAllocType, 
              void* pvData, 
              size_t nSize,
              int nBlockUse, 
              long lRequest, 
              const unsigned char *szFileName, 
              int nLine )
{
  /// Very Importaint !! 
  /// infinite recursion if this is removed !!
  /// _CRT_BLOCK must not do any thing but return TRUE
  /// even calling printf in the _CRT_BLOCK will cause
  /// infinite recursion
  if ( nBlockUse == _CRT_BLOCK )
    return( TRUE );
  switch(nAllocType)
  {
  case _HOOK_ALLOC:
  case _HOOK_REALLOC:
    // zero initialize the allocated space.
    memset(pvData,0,nSize);
    break;
  case _HOOK_FREE:
    break;
  }
  return TRUE;
}

Это на самом деле непреднамеренно дает мне решение, которое я хочу: я могу установить pvData в NULL на _HOOK_FREE и не столкнуться с проблемами с 0xFEEEFEEE для моего адреса указателя.

Jeff Hubbard 16.09.2008 09:16

Обязательно -1, потому что вы просто показываете оператору, как наиболее эффективно выстрелить себе в ногу. delete просто не обязан менять указатель на что-либо.

j_random_hacker 06.01.2011 16:12
msdn.microsoft.com/en-us/library/820k4tb8(v=vs.100).aspx states that pvData is always NULL 'when the triggering allocation type is _HOOK_ALLOC or _HOOK_REALLOC because the memory block has not been allocated yet'. So this can't work.
Randy Voet 21.10.2013 10:15

если вы используете malloc, он ни к чему не инициализирует память. вы получите что угодно. если вы хотите выделить блок и инициализировать его значением 0, используйте calloc, который похож на malloc только с инициализацией (параметр размера элемента, который вы устанавливаете на 1, если хотите эмулировать malloc). вы должны прочитать о calloc перед его использованием, так как он имеет некоторые незначительные отличия.

http://wiki.answers.com/Q/What_is_the_difference_between_malloc_and_calloc_functions

На самом деле это очень хорошая функция в VC++ (и я верю в другие компиляторы), потому что она позволяет вам видеть нераспределенную память для указателя в отладчике. Я дважды подумаю, прежде чем отключать эту функцию. Когда вы удаляете объект в C++, вы должны установить указатель на NULL на случай, если что-то позже попытается удалить объект снова. Эта функция позволит вам обнаружить места, где вы забыли установить указатель на NULL.

Почему бы не создать свой собственный #define и не выработать привычку его использовать?

Т.е.

#define SafeDelete(mem) { delete mem; mem = NULL; }
#define SafeDeleteArray(mem) { delete [] mem; mem = NULL; }

Очевидно, вы можете называть это как угодно. deleteZ, deletesafe, все, что вам удобно.

Макросы, которые используют свой параметр дважды, всегда являются предупреждающим знаком. while (ptr! = end ({delete ptr ++;} ... while (ptr! = end) {SafeDelete (ptr ++)}; упс. Мы могли бы найти другие примеры. Лучше была бы встроенная функция, которая принимает ссылку.

CashCow 31.01.2012 15:37

Ты говоришь:

I create and initialize a variable (via new), and that all goes just fine. When I free it (via delete), it sets the pointer to 0xFEEEFEEE instead of NULL. When I insert a proper check for NULL, as all good programs that manage their own memory should, I come up with problems as 0xFEEEFEEE passes a NULL check without problems.

Даже процедуры кучи отладки MSVC не изменят значение удаляемого указатель - значение удаляемого указателя не изменится (даже на NULL). Похоже, вы обращаетесь к указателю, который принадлежит только что удаленному объекту, что является простой и простой ошибкой.

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

@ Джефф Хаббард (комментарий):

This actually inadvertently provides me with the solution I want: I can set pvData to NULL on _HOOK_FREE and not run into problems with 0xFEEEFEEE for my pointer address.

Если это работает для вас, это означает, что вы читаете освобожденную память, когда проверяете указатель NULL (т. Е. Сам указатель находится в освобожденной вами памяти).

Это ошибка.

«Решение», которое вы используете, просто скрывает, а не исправляет ошибку. Когда эта освобожденная память когда-либо будет выделена для чего-то другого, вы внезапно будете использовать неправильное значение в качестве указателя на неправильную вещь.

Происходит сбой моего кода при отладочной компиляции, но успешный при выпускной компиляции. Я проверил это в отладчике, и мои указатели устанавливаются на 0xFEEEFEEE после того, как я вызываю для них delete. Опять же, тот же код при выпуске не дает сбоев и ведет себя так, как ожидалось.

Jeff Hubbard 17.09.2008 03:07

Ваш код случайно работает в режиме релиз-компиляции. Значение 0xFEEEFEEE - разработан, чтобы вызвать проблемы, если вы обращаетесь к памяти, которая была освобождена, поскольку это ошибка в вашем коде.

Head Geek 06.12.2008 20:13

@ [Джефф Хаббард]:

What's happening is my code crashes under a debug compilation, but succeeds under a release compilation. I've checked it under a debugger and my pointers are getting set to 0xFEEEFEEE after I call delete on them. Again, same code on release doesn't crash and behaves as expected.

Это очень странное поведение - я все еще убежден, что, вероятно, существует скрытая ошибка, которую скрывает обходной путь _CrtSetAllocHook().

Подпись 0xFEEEFEEE используется диспетчером кучи ОС для обозначения освобожденной памяти (см. http://www.nobugs.org/developer/win32/debug_crt_heap.html). Не могли бы вы опубликовать репро-код и указать, какую именно версию компилятора вы используете?

Если он работает в режиме выпуска, то это из-за удачи.

Майк Б. прав, предполагая, что исправление отладки скрывает ошибку. В режиме выпуска используется указатель, который был освобожден, но не установлен на NULL, а память, на которую он указывает, все еще «действительна». В какой-то момент в будущем изменится распределение памяти, или изображение памяти изменится, или что-то заставит «действительный» блок памяти стать «недействительным». В этот момент ваша сборка выпуска начнет давать сбой. Переход в режим отладки для поиска проблемы будет бесполезным, потому что режим отладки был «исправлен».

Я думаю, мы все согласны с тем, что следующий код не должен работать.

char * p = new char[16];     // 16 bytes of random trash
strcpy(p, "StackOverflow");  // 13 characters, a '\0' terminator, and two bytes of trash
delete [] p;                 // return 16 bytes to the heap, but nothing else changes;

if (p != NULL)               // Why would p be NULL?  It was never set to NULL
    ASSERT(p[0] == 'S');     // In debug, this will crash, because p = 0xfeeefeee and 
                             // dereferencing it will cause an error.
                             // Release mode may or may or may not work, depending on
                             // other memory operations

Как сказано почти в каждом другом плакате, указатели должны быть установлены на NULL после вызова delete. Независимо от того, делаете ли вы это сами, или используете boost, или другую оболочку, или даже макрос в этом потоке, зависит от вас.

What's happening is my code crashes under a debug compilation, but succeeds under a release compilation.

Сборка релиза выйдет из строя на машине клиента. Так всегда бывает.

I've checked it under a debugger and my pointers are getting set to 0xFEEEFEEE after I call delete on them.

Указатели не изменяются после того, как вы вызываете для них удаление. Это память, на которую они указывают, устанавливается в 0xfeeefeee, 0xfeeefeee, ..., 0xfeeefeee.

Если вы заметили, что ваша программа считывает данные из освобожденной памяти (что удобно обозначено шаблоном 0xfeeefeee в сборке DEBUG), у вас есть ошибка.

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

Другие вопросы по теме