Где выделена моя память, стек или куча, могу ли я найти ее во время выполнения? - PullRequest
1 голос
/ 07 июня 2010

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

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

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 вместо выделения значения памяти кучи он указывает на строковый литерал.

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

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

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

Ответы [ 7 ]

2 голосов
/ 07 июня 2010

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

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

Если вы используете 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
2 голосов
/ 07 июня 2010

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

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

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

Вы на самом деле имеете дело с 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. Вы можете использовать его, только если точно знаете, на какую кучу была выделена память (на случай, если это куча памяти).
2 голосов
/ 07 июня 2010

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

2 голосов
/ 07 июня 2010

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

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

1 голос
/ 07 июня 2010

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

0 голосов
/ 07 июня 2010

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

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

0 голосов
/ 07 июня 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...