Подтвердить что ты не робот

Как узнать, указывает ли указатель на кучу или стек?

Пример:

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 его. Я собираюсь избегать программирования мусора

4b9b3361

Ответ 1

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

Ответ 2

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

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

if ((ptrStack < stackBase) && (ptrStack > (stackBase - stackSize)))
    isStackPointer1 = TRUE;

Ответ 3

Единственное "хорошее" решение, о котором я могу думать, это перегрузить 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;
}

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

Ответ 4

Хорошо, вытащите свою ассемблерную книгу и сравните свой адрес указателя с указателем стека:

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

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

Ответ 5

вот он, работает для 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 (означает, что это куча)

Ответ 6

В основных операционных системах стек растет сверху, а куча растет снизу. Таким образом, вы можете эвристически проверить, находится ли адрес за пределами большого значения, для некоторого определения "большой". Например, в моей 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;
}

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

Ответ 7

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

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

Ответ 8

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

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

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

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

Ответ 9

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

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

например.

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;
};

Ответ 10

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

Ответ 11

здесь универсальный способ сделать это в Windows с помощью TIP:

bool isStack(void* x)
{
    void* btn, *top;
    _asm {
        mov eax, FS:[0x08] 
        mov btn, eax
        mov eax, FS:[0x04] 
        mov top, eax
    }
    return x < top && x > btn;
}

void func()
{

    int i;

    bool b1;
    bool b2;

    b1 = isStack(&i);
    b2 = isStack(&si);

    return;
}

Ответ 12

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

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

class A {
   int data;
};

class B {
 public:
   A *giveMeAnA() { return &anA; }
   int data;
   A anA;
};

void foo()
{
   B *b = new B;
   A *a = b->giveMeAnA();
}

В приведенном выше коде a в foo заканчивается указатель на объект в куче, который не был выделен new. Если ваш вопрос действительно "Как узнать, могу ли я называть delete на этом указателе". перегрузка operator new, чтобы сделать что-то сложное, может помочь вам ответить на этот вопрос. Я все еще думаю, что если вы должны задать этот вопрос, вы сделали что-то очень плохое.

Ответ 13

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

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

Посмотрите на этот код (для примера не проверяйте ошибки):

class A
{
int *mysweetptr;

A()
{
   mysweetptr = 0; //always 0 when unalloc'd
}

void doit()
{
   if( ! mysweetptr)
   {
       mysweetptr = new int; //now has non-null value
   }
}

void undoit()
{
   if(mysweetptr)
   {
      delete mysweetptr;
      mysweetptr = 0;  //notice that we reset it to 0.
   }
}

bool doihaveit()
{
   if(mysweetptr)
     return true;
   else 
     return false; 
}
~A()
{
   undoit();
}
};

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

Ответ 14

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