Вопрос

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

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

Это было полезно?

Решение

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

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

Ты можешь изменять тот заливка по умолчанию для окон ОТЛАДКА среды выполнения используя такой крючок-распределитель.Это будет работать только с объектом, выделенным в 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;
}

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

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

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

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

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

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

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

Ты говоришь:

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

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

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

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

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

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

Это ошибка.

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

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

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

Майк Б прав, полагая, что исправление отладки скрывает ошибку.В режиме выпуска используется указатель, который был освобожден, но не установлен в значение. 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 или какую-то другую обертку или даже макрос в этой теме — решать вам.

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

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

Я проверил его под отладчиком, и мои указатели устанавливаются на 0xfeeefeee после того, как я позвоню в Delete на них.

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

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

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

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

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

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

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

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

int *ptr=0;

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

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

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

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

Т.е.

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top