Как узнать, указывает ли указатель на кучу или стек? - PullRequest
27 голосов
/ 12 июля 2010

Пример:

bool isHeapPtr(void* ptr)
{
     //...
}

int iStack = 35;
int *ptrStack = &iStack;
bool isHeapPointer1 = isHeapPtr(ptrStack); // Should be false
bool isHeapPointer2 = isHeapPtr(new int(5)); // Should be true
/* I know... it is a memory leak */

Почему, я хочу знать это:

Если у меня в классе указатель на член, и я не знаю, является ли указывающий объект новым. Тогда я должен использовать такую ​​утилиту, чтобы узнать, нужно ли мне delete указатель.

Но:
Мой дизайн еще не сделан. Итак, я запрограммирую это так, как я всегда должен delete. Я собираюсь избегать мусорного программирования

Ответы [ 14 ]

30 голосов
/ 12 июля 2010

Нет способа сделать это - и если вам нужно это сделать, значит, что-то не так с вашим дизайном.Существует обсуждение, почему вы не можете сделать это в Более эффективный C ++ .

15 голосов
/ 12 июля 2010

Боюсь, что в общем случае вам не повезло - поскольку указатели могут иметь любое значение, их невозможно отличить.Если вы знали начальный адрес и размер вашего стека (например, из TCB во встроенной операционной системе), вы можете сделать это.Что-то вроде:

stackBase = myTCB->stackBase;
stackSize = myTCB->stackSize;

if ((ptrStack < stackBase) && (ptrStack > (stackBase - stackSize)))
    isStackPointer1 = TRUE;
12 голосов
/ 12 июля 2010

Единственное «хорошее» решение, которое я могу придумать, - это перегрузить operator new для этого класса и отследить его.Примерно так (скомпилированный мозгом код):

class T {
public:    
    void *operator new(size_t n) {
        void *p = ::operator new(n);
        heap_track().insert(p);
        return p;
    }

    void operator delete(void* p) {
        heap_track().erase(p);
        ::operator delete(p);
    }

private:

    // a function to avoid static initialization order fiasco
    static std::set<void*>& heap_track() {
        static std::set<void*> s_;
        return s_;
    }

public:
    static bool is_heap(void *p) {
        return heap_track().find(p) != heap_track().end();
    }
};

Тогда вы можете сделать что-то вроде этого:

T *x = new X;
if(T::is_heap(x)) {
    delete x;
}

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

8 голосов
/ 12 июля 2010

Что ж, достаньте свою книгу ассемблера и сравните адрес вашего указателя с указателем стека:

int64_t x = 0;
asm("movq %%rsp, %0;" : "=r" (x) );
if ( myPtr < x ) {
   ...in heap...
}

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

5 голосов
/ 12 июля 2010

вот оно, работает для MSVC:

#define isheap(x, res) {   \
void* vesp, *vebp;     \
_asm {mov vesp, esp};   \
_asm {mov vebp, ebp};    \
res = !(x < vebp && x >= vesp); }

int si;

void func()
{
    int i;
    bool b1;
    bool b2;
    isheap(&i, b1); 
    isheap(&si, b2);
    return;
}

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

3 голосов
/ 12 июля 2010

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

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

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

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

3 голосов
/ 12 июля 2010

Во-первых, зачем вам это знать? Какую реальную проблему вы пытаетесь решить?

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

3 голосов
/ 12 июля 2010

В основных операционных системах стек растет сверху, а куча - снизу. Таким образом, вы могли бы эвристически проверить, не превышает ли адрес большое значение, для некоторого определения "большого". Например, в моей 64-битной системе Linux работает следующее:

#include <iostream>

bool isHeapPtr(const void* ptr) {
  return reinterpret_cast<unsigned long long int>(ptr) < 0xffffffffull;
}

int main() {
  int iStack = 35;
  int *ptrStack = &iStack;
  std::cout << isHeapPtr(ptrStack) << std::endl;
  std::cout << isHeapPtr(new int(5)) << std::endl;
}

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

2 голосов
/ 12 июля 2010

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

2 голосов
/ 12 июля 2010

Несмотря на громкие заявления об обратном, вполне можно делать то, что вы хотите, в зависимости от платформы. Однако то, что что-то возможно, автоматически не делает это хорошей идеей. Простое правило стека == нет удаления, иначе == удаление вряд ли будет работать хорошо.

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

, например

class CSomething
{
public:
    CSomething()
    : m_pBuffer(new char[128])
    , m_bDeleteBuffer(true)
    {
    }

    CSomething(const char *pBuffer)
    : m_pBuffer(pBuffer)
    , m_bDeleteBuffer(false)
    {
    }

    ~CSomething()
    {
        if (m_bDeleteBuffer)
            delete [] m_pBuffer;
    }

private:
    const char *m_pBuffer;
    bool        m_bDeleteBuffer;
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...