如何在此自定义堆栈实现中正确分配更多内存?

Posted

技术标签:

【中文标题】如何在此自定义堆栈实现中正确分配更多内存?【英文标题】:How do I correctly allocate more memory in this custom stack implementation? 【发布时间】:2016-11-04 09:12:06 【问题描述】:

我正在努力使每次超出大小时我的堆栈大小都会翻倍。我需要创建一个新堆栈来保存旧堆栈,但大小要翻倍。需要删除旧堆栈。下面的代码不断给我错误

"Stack(17854,0x7fff77cd0300) malloc: * 对象 0x1001054b0 的错误:被释放的指针未被分配 * 在 malloc_error_break 中设置断点进行调试"

此外,每次运行程序时生成的随机数都是相同的。帮助!

#include <iostream>
using namespace std;

const int DEFAULT_SIZE = 100;

template< class T >
class Stack 
public:
    Stack( int = 10 );  // default constructor (stack size 10)
    // destructor
    ~Stack() 
            delete [] stackPtr;
    

    bool push( const T& );
    bool pop( T& );
    int pop();

    // determine whether Stack is empty
    bool isEmpty() const 
            return top == -1;
    

    // determine whether Stack is full
    bool isFull() const  
            return top == size - 1;
    

private:
    int size;     // # of elements in the stack
    int top;      // location of the top element
    T *stackPtr;  // pointer to the stack
;

// constructor
template< class T >
Stack< T >::Stack( int s ) 
    size = s > 0 ? s : 10;
    top = -1;  // Stack initially empty
    stackPtr = new T[ size ]; // allocate memory for elements


template< class T >
bool Stack< T >::push( const T &pushValue ) 
    if ( !isFull() ) 
        stackPtr[ ++top ] = pushValue;
        return true;
    

    T *newPtr = new T[size*2];
    newPtr = stackPtr;
    delete [] stackPtr;
    return true;


template< class T >
bool Stack< T >::pop( T &popValue ) 
    if ( !isEmpty() ) 
        popValue = stackPtr[ top-- ];  // remove item from Stack
        return true;
    

    return false;


template <class T>
int Stack< T >::pop() 
    return stackPtr[--size];


int main() 
    Stack<int> s;
    int i = 0;
    for (i=0; i < DEFAULT_SIZE; i++) 
        s.push( rand() % 100 +1 );
    

    for (i=0; i < DEFAULT_SIZE; i++) 
        cout << s.pop() << " , ";
        if (i % 20 == 0) 
            cout << endl;
        
    

【问题讨论】:

关于第一个问题,如果您提出minimal reproducible example,您将有更好的时间解决问题。当前代码中有很多与问题无关的内容。关于第二个问题,请阅读rand 的一些文档。 您对“满时重新分配”代码的处理非常糟糕,那里有两个错误。 std::stack 听起来有吸引力吗?坚持下去,你最终会得到它。 【参考方案1】:

看看这段代码,它来自您的push 实现(这是您分配更多内存的部分):

1: T *newPtr = new T[size*2];
2: newPtr = stackPtr;
3: delete [] stackPtr;
4: return true;

从视觉上看,这就是正在发生的事情。在第 1 行之前,情况如下所示:

 +----------+       +-----+-----+-----+-----+
 | stackPtr | ----> | 137 | 271 | 281 | 284 |
 +----------+       +-----+-----+-----+-----+

执行第 1 行后,情况如下:

 +----------+       +-----+-----+-----+-----+
 | stackPtr | ----> | 137 | 271 | 281 | 284 |
 +----------+       +-----+-----+-----+-----+
 +----------+       +-----+-----+-----+-----+-----+-----+-----+-----+
 |  newPtr  | ----> |  ?  |  ?  |  ?  |  ?  |  ?  |  ?  |  ?  |  ?  |
 +----------+       +-----+-----+-----+-----+-----+-----+-----+-----+

执行第 2 行后,情况如下:

 +----------+       +-----+-----+-----+-----+
 | stackPtr | --+-> | 137 | 271 | 281 | 284 |
 +----------+   |   +-----+-----+-----+-----+
 +----------+   |   +-----+-----+-----+-----+-----+-----+-----+-----+
 |  newPtr  | --+   |  s  |  o  |     |  a  |  l  |  o  |  n  |  e  |
 +----------+       +-----+-----+-----+-----+-----+-----+-----+-----+

哎呀。你刚刚遗弃了一堆内存。

执行第 3 行后,情况如下:

 +----------+        
 | stackPtr | --+->    kablooie! deleted memory.
 +----------+   |
 +----------+   |
 |  newPtr  | --+
 +----------+     

请注意,当您完成后,您会得到孤立的内存(所有的?),并且您的 stackPtr 变量现在指向死内存。哎呀。

要解决此问题,您需要进行一些更改。首先,当你写的时候

newPtr = stackPtr;

我的感觉是您打算将所有元素从旧数组复制到新数组。不幸的是,如上图所示,您所写的并没有达到您的预期。要解决此问题,您需要一次显式地移动一个元素。考虑使用 for 循环来执行此操作 - 一次从 stackPtr 读取一个元素并写入 newPtr 中的相应条目。

其次,您需要更改stackPtr,以便在炸毁之前分配的内存后,让它指向新分配的内存。一种方法是写

stackPtr = newPtr;

在您为stackPtr 释放内存之后。

这里还有另一个问题。请注意,在分配新数组后,您实际上从未更新 size。这意味着虽然您将获得一个全新的阵列来使用,但您实际上不会记得它有多大。因此,在完成其他所有操作后,请确保更新 size,使其比以前大一倍。

代码中可能存在其他问题,但我怀疑这将有助于您入门。需要记住的一些事情:

    使用指针绘制图片永远不会有什么坏处。 注意不要将“分配指针”与“复制数组元素”混淆。 记得做好所有必要的簿记工作。

祝你好运!

【讨论】:

还有一个错误是代码不会在内存不足的情况下进行推送。【参考方案2】:
T *newPtr = new T[size*2];
newPtr = stackPtr;
delete [] stackPtr;
return true;

这以stackPtr 结束,指向它现在已删除的原始内存。您也永远不会真正将旧内存的内容移动到新内存。它应该是这样的:

T* newPtr = new T[size*2];
std::move(stackPtr, stackPtr + size, newPtr);
delete[] stackPtr;
stackPtr = newPtr;

关于rand每次都返回相同的东西;你永远不会播种随机生成器。您需要在程序开始时调用srand,然后再调用rand

【讨论】:

【参考方案3】:

这段代码显然是错误的:

T *newPtr = new T[size*2];
newPtr = stackPtr;
delete [] stackPtr;

你分配一个新指针,然后立即丢弃它,然后删除它。所以新分配的内存泄漏了,stackPtr 已经被释放并且失效了。您也不会在 size 更改时更新它(好吧,如果您没有像筛子那样泄漏内存,它会被更改),并且您有时使用 size,而您可能是指 top

【讨论】:

以上是关于如何在此自定义堆栈实现中正确分配更多内存?的主要内容,如果未能解决你的问题,请参考以下文章

堆和堆栈内存是如何管理、实现和分配的?

C ++如何在堆栈上动态分配内存?

Material UI - 提供给 ButtonBase 的 `component` 道具无效。请确保在此自定义组件中呈现 children 道具

使用 'push' 或 'sub' x86 指令时如何分配堆栈内存?

C++:编译器如何知道为每个堆栈帧分配多少内存?

在C语言中,如何给函数分配内存?