【问题标题】:array wrapper corrupts stack数组包装器损坏堆栈
【发布时间】:2020-10-03 09:49:29
【问题描述】:

我的项目是一个动态数组包装器,例如 std::vector。这就是它的工作原理:

  • 添加新元素时,如果为0,则分配内存(malloc),如果不为0,则使用新大小(realloc)重新分配。大小是元素的数量*字体大小

  • 当获取一个已经添加的元素时,我通过将其索引乘以类型的大小并将其添加到分配内存的地址来计算地址

注意:我自己编写和读取内存,没有 memcpy 或 memset 之类的功能。这是我的项目所必需的。我应该可以这样做,如果可以,请不要提及(除非我执行错误,在这种情况下,请提及)

当我尝试使用 get(int index) 函数读取添加的元素时,我会收到“变量周围的堆栈已损坏”或“读取访问冲突”错误,具体取决于我尝试执行此操作的方式。

我在网上阅读了一下,发现我可能已经用 malloc 以某种方式破坏了堆。还阅读了我可以通过名为“valgrind”的东西找出错误所在,但它似乎只适用于 linux,而且我使用的是 windows。

这是我的代码(它被重写,所有错误检查都被删除以使其更小)。我得到错误的地方被评论:

template<class T>
class darr
{
public:
    darr(void) {}
    ~darr(void) {
        erase(); dealloc();
    }

    bool alloc(int elemc) {
        this->elemc = elemc;
        this->size = (elemc * sizeof(T));
        this->end = (this->start + this->size);

        if (this->start)
        {
            this->start = (T*)(realloc(this->start, this->size));

            if (this->start)
            {
                this->end = (this->start + this->size);
                return true;
            }
        }
        else
        {
            this->start = (T*)(malloc(this->size));

            if (this->start)
            {
                this->end = (this->start + this->size);
                return true;
            }
        }

        return false;
    }

    bool erase(void)
    {
        for (int i = 0; i <= this->size; ++i)
        {
            *(unsigned long*)(this->start + i) = 0;
        }

        return true;
    }

    bool dealloc(void)
    {
        free(this->start);

        return true;
    }

    bool add(T obj)
    {
        void* end_temp = 0;

        if (this->end) { end_temp = this->end; }

        if (true == this->alloc(++this->elemc))
        {
            end_temp = this->end;

            for (int i = 0; i <= sizeof(obj); ++i)
            {
                *(unsigned long*)((unsigned long)(end_temp)+i) = *(unsigned long*)((unsigned long)(&obj) + i);
            }
        }

        return true;
    }

    T get(int i)
    {
        unsigned long siz = sizeof(T);
        void* i_addr = this->start + (i * siz);

        //T tempobj = 0;
        T* tempobj = (T*)(malloc(sizeof(T)));
        // without malloc - stack around var corrupted (happnens at last index in for loop, no matter what index it is)
        // with malloc - read access violation
        for (int i = 0; i <= siz; ++i)
        {
            *(unsigned long*)((unsigned long)(&tempobj)+i) = *(unsigned long*)((unsigned long)(i_addr)+i);
        }

        return *tempobj;
    }
private:
    T * start;
    void* end;
    int elemc, size;
};

【问题讨论】:

标签: c++ memory-management heap-memory heap-corruption


【解决方案1】:

让我们分解代码并慢慢修复它。从构造函数开始,一切都应该被初始化。尽快初始化变量非常重要:

    darr() {
        start = nullptr;
        end = nullptr;
        elemc = 0;
        size = 0;
    }

现在让我们看看方法add。它应该怎么做?向容器中添加一个元素并保留已经存在的元素。它现在在做什么?让我们看看:

  1. 创建一个临时的void* 指针。
void* end_temp = 0;
  1. 检查是否end != nullptr,我们将end分配给end_temp
if (this->end) { end_temp = this->end; }
  1. 分配内存并增加elemc(我假设是元素计数)
if (true == this->alloc(++this->elemc))
  1. 下一个??我不知道。我也不确定这与将元素添加到容器的简单任务有什么关系:
{
     end_temp = this->end;

            for (int i = 0; i <= sizeof(obj); ++i)
            {
                *(unsigned long*)((unsigned long)(end_temp)+i) = *(unsigned long*)((unsigned long)(&obj) + i);
            }
        }

        return true;
    }

让我们简化并做我们想要的,即添加元素:

        if (true == this->alloc())
        {
            start[size] = obj;
            this->size++;
        }

alloc() 不再接受参数,因为它总是增加 1。size 是容器中元素的数量。我们只是将元素分配给最后一个索引,并将大小增加 1。

现在让我们看看get()。它需要做什么?获取索引处的元素。简单写一下吧:

T get(int i) const
{
    if (i < size)
        return start[i];
}

【讨论】:

  • 我确实把它复杂化了。 add 中的代码是在考虑“复制 memcpy 函数”的情况下编写的,所以我尝试逐字节地将每个字节从源复制到目标,但我将“逐字节”部分作为我后来才知道。关于初始化,现在不是所有东西都被编译器自动初始化了吗?
  • 请记住,对于一个字节一个字节,您应该始终使用char*reinterpret_cast&lt;&gt; 只能与char* 一起使用,因为它几乎会毁掉你的整个程序。参考这个:gist.github.com/shafik/848ae25ee209f698763cffee272a58f8stackoverflow.com/questions/98650/…
  • 不,只有全局变量和静态变量如果是像int/long这样的本机类型,它们会被初始化为0。其他一切都需要初始化,局部变量,类成员
  • 我现在明白了。我个人不太了解这个 UB 的东西,你给我的链接更多地讨论了编译器如何误解一行或更多代码并导致 UB,并不是说使用不安全代码不可能正确处理(或者至少这是我从中得到的)。是的,多亏了 churill,我才意识到我是如何弄乱我禁用的 memcpy 克隆的。非常感谢您的帮助,兄弟!
  • 你现在可能不是什么都明白了,但是时不时的回头看这些帖子,每次你都会明白的更多。
猜你喜欢
  • 1970-01-01
  • 2016-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-07
  • 2010-10-17
相关资源
最近更新 更多