2010-06-07 3 views
1

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

Давайте посмотрим на эту программу ...

Case 1: 

char *MyData = new char[20]; 
_tcscpy(MyData,"Value"); 
. 
. 
. 
delete[] MyData; MyData = NULL; 


Case 2: 
char *MyData = new char[20]; 
MyData = "Value"; 
. 
. 
. 
delete[] MyData; MyData = NULL; 

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

Теперь, когда мы делаем удаление, он будет аварийно завершен, AS EXPECTED, так как он не пытается удалить кучу памяти. Есть ли способ узнать, где указатель указывает на кучу или стек?

К этому программисту

  • не буду пытаться удалить любую стековую память
  • Он может исследовать почему это ponter, что указывал на динамическую память изначально, сделана для обозначения локальных литералов? Что случилось с кучей памяти посередине? Это делается для указания другим указателем и удаления в другом месте и все такое?
+1

Вам повезло, что вы не получили аварии в первом случае - вы используете 'delete' вместо' delete [] '(или еще лучше,' std :: string'). – avakar

+1

@avakar: Я бы сказал, что ему не повезло - использование 'delete' вместо' delete [] 'является неопределенным поведением с неограниченными последствиями. Никто не знает наверняка, что на самом деле происходит, если реализация очень тщательно изучена. И даже после этого код в unportable. – sharptooth

+0

@avakar: Извините. Я набрал его напрямую. Теперь у меня это изменилось. Спасибо, что указали это. – AKN

ответ

2

Есть ли способ узнать, где указатель указывает на кучу или стек?

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

Если вы используете boost::shared_ptr в качестве примера вы можете сделать это:

template<typename T> void no_delete(T* ptr) { /* do nothing here */ } 

class YourDataType; // defined elsewhere 
boost::shared_ptr<YourDataType> heap_ptr(new YourDataType()); // delete at scope end 

YourDataType stackData; 
boost::shared_ptr<YourDataType> stack_ptr(&stackData, &no_delete); // never deleted 
1

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

2

Как только вам нужны эти знания, вы уже потеряли. Зачем? Потому что, даже если вы опустили неправильное удаление [], у вас все еще есть утечка памяти.

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

+0

Да, как вы правильно сказали, я указал, что в программе есть утечка памяти. Мой вопрос в том, есть ли у нас какие-либо способы выяснить, изменился ли указатель, который ссылался на одну кучную память, где и что случилось с более старым. Для простейших программ, подобных этому, его легко заметить. Но для крупных проектов технического обслуживания это утомительно. Вы бы согласились, если бы вы вообще не работали над проектами технического обслуживания. – AKN

+2

Вот почему мы используем std :: strings, std :: vectors, избегаем указателей, когда это не нужно, и если это действительно необходимо, мы используем интеллектуальные указатели. – Nikko

+1

AKN: Nikko поднимает здесь хороший момент. Возможно, в рамках вашей работы по поддержке вам придется реорганизовать код для использования интеллектуальных указателей или строк C++. По крайней мере, это то, что я делаю, когда мне приходится включать старый код C в нашу структуру. – ypnos

0

В случае 2 MyData = «Значение» вызывает утечку памяти, поскольку больше не ссылается на память, возвращаемую с новой.

2

В стандартном C++ нет способа определить, указывает ли указатель на динамически выделенную память или нет. И обратите внимание, что строковые литералы не выделяются в стеке.

+0

@Neil: «Строковые литералы не выделяются в стеке». Если не стек, можете ли вы рассказать, куда он идет? – AKN

+0

@AKN Страндард не говорит. Он говорит, что время жизни литерала - это программа, поэтому они не могут быть помещены в стек. – 2010-06-07 09:30:35

+0

Строковые литералы @AKN являются частью исполняемого файла. Они находятся в текстовом сегменте вместе с кодом (в зависимости от двоичного формата, конечно). – JeremyP

2

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

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

Но если вы настаиваете, тем не менее есть некоторые способы узнать, какая память принадлежит типу.

Вы на самом деле иметь дело с 3-мя типами памяти

  • Stack
  • Heap
  • Global

Например:

char* p = new char[10]; // p is a pointer, points to heap-allocated memory 

char* p = "Hello, world!"; // p is a pointer, points to the global memory 

char p[] = "Hello, world!"; // p is a buffer allocated on the stack and initialized with the string 

Теперь давайте различать их. Я опишу это с точки зрения Windows API и ассемблера x86 (так как это то, что я знаю :))

Давайте начнем с памяти стека.

bool IsStackPtr(PVOID pPtr) 
{ 
    // Get the stack pointer 
    PBYTE pEsp; 
    _asm { 
     mov pEsp, esp 
    }; 

    // Query the accessible stack region 
    MEMORY_BASIC_INFORMATION mbi; 
    VERIFY(VirtualQuery(pEsp, &mbi, sizeof(mbi))); 

    // the accessible stack memory starts at mbi.BaseAddress and lasts for mbi.RegionSize 
    return (pPtr >= mbi.BaseAddress) && (pPtr < PBYTE(mbi.BaseAddress) + mbi.RegionSize); 
} 

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

Глобальная память

bool IsGlobalPtr(PVOID pPtr) 
{ 
    MEMORY_BASIC_INFORMATION mbi; 
    VERIFY(VirtualQuery(pPtr, &mbi, sizeof(mbi))); 

    // Global memory allocated (mapped) at once for the whole executable 
    return mbi.AllocationBase == GetModuleHandle(NULL); 
} 

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

Heap

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

Но здесь есть большая двусмысленность.

Вы должны знать, что вы различные реализации кучи (например, сырой кучи Windows, доступ по HeapAlloc/HeapFree или CRT-обернутой malloc/free или new/delete).

Вы можете удалить такой блок с помощью оператора delete, только если вы точно знаете, что это либо указатель на стек/глобальный указатель, либо он был выделен через new.

В заключение:

  1. Это своего рода извращенец трюк. Не следует использовать в целом. Лучше предоставить дополнительную информацию указателем, который рассказывает, как его выпустить.
  2. Вы можете использовать его, только если вы точно знаете, на какую кучу выделена память (в случае, если это куча памяти).
+0

Верьте или нет, не все используют Windows. – 2010-06-07 11:03:50

+0

Хорошо, я не настаиваю на использовании Windows. Вероятно, есть способы сделать это и в Linux. – valdo

+0

Он пропускает файлы с отображением памяти, включая анонимное (файл подкачки) и память, полученную от других (например, COM-строки). И threadd-локальные переменные. И, возможно, даже больше. – MSalters

0

Там нет простого способа или стандартным способа сделать это. Вы можете перехватить функцию (ы) распределения кучи и поместить каждую выделенную память в список. Ваша функция «IsHeap» должна проверить, была ли зона передана функции той, которая указана в списке. Это всего лишь намек - почти невозможно сделать это кросс-платформенным способом.

Но опять же - зачем вам это нужно?

Смежные вопросы