【问题标题】:C++ deleting first element in array of pointers effects later elementsC ++删除指针数组中的第一个元素会影响后面的元素
【发布时间】:2019-05-17 06:40:00
【问题描述】:

对于一个实验室任务,我正在研究堆的数组实现。我有一个PrintJob * 类型的数组。我面临的问题是我尝试使用delete 删除的第一个元素arr[0] 奇怪地修改了数组的第二个元素。最终,该元素到达堆的头部,删除它会导致 SIGABRT。

我原本想也许直接从数组中删除它,delete arr[0] 发出某种类型的错误,因为我会反复调用delete arr[0];尽管如此,我在删除arr[0] 后立即将其更新为下一个最大的孩子。所以,我尝试将它存储到一个临时变量中,然后将其删除:

void dequeue() {
    PrintJob *temp = arr[0];
////    delete arr[0];
    trickleUp(0);
    delete temp;
}

但我很快意识到我的努力没有任何意义。我知道当程序尝试两次删除动态分配的实体时会发生 SIGABRT,但除了第一个元素之外,我从未触及任何其他元素。所以我很困惑为什么第二个元素被垃圾值填充,然后抛出 SIGABRT。

这是我使用的其他一些代码:

这个函数由上面的函数调用,控制将当前索引(n's)的最大子元素移动到它的位置的过程。它根据要求递归地执行此操作。

void trickleUp(int n) {

    int c = getChild(n, true);  // get the greater child

    if (c >= MAX_HEAP_SIZE) {   // if the
        PrintJob *temp = arr[n];
////        delete arr[n];
        arr[n] = nullptr;
        delete temp;
        return;
    }

    arr[n] = arr[c];    // update the old node
    trickleUp(c); // move to the next element in the tree;
}

getChild() 是由前一个函数调用的函数,旨在返回当前索引n 的最大子索引(ln:左节点,rn:右节点)。

int getChild(int n, bool g) {

    int ln = (2 * n) + 1, rn = (2 * n) + 2, lp = -1, rp = -1;

    if (ln < MAX_HEAP_SIZE && arr[ln]) {
        lp = arr[ln]->getPriority();
    }

    if (rn < MAX_HEAP_SIZE && arr[rn]) {
        rp = arr[rn]->getPriority();
    }

    return  ( !((lp > rp) ^ g) ? ln:rn );
}

我已经多次检查代码,也没有看到任何其他逻辑错误,当然,直到这个问题得到解决并且我能够用额外的样本进行测试,我才能真正说出.如果您想自己编译,这里是所有其余代码的链接。我也附上了一个makefile。 https://drive.google.com/drive/folders/18idHtRO0Kuh_AftJgWj3K-4OGhbw4H7T?usp=sharing

【问题讨论】:

  • 请在没有外部链接的问题中提供minimal reproducible example
  • arr[n] = arr[c]; 看起来有点可疑,你现在有相同的指针存储在两个不同的地方?
  • 直到我将 arr[c] 更新为下一行 trickleUp(c) 中的下一个最大的孩子。你还建议我改吗?
  • 您需要调试您的代码,但由于您怀疑双重释放,那么您的代码中重复指针的部分可能是一个很好的起点
  • 请发帖minimal reproducible example。如果做不到这一点,我可以提供毫无根据的猜测和挥手。

标签: c++ arrays heap delete-operator sigabrt


【解决方案1】:

用一些打印来检测您的代码会产生以下输出:

set 0
set 1
set 2
set 3
set 4
swap 1, 4
swap 0, 1
copy 1 to 0
copy 4 to 1
delete 4
copy 2 to 0
copy 6 to 2
delete 6
copy 2 to 0
copy 6 to 2
delete 6
copy 2 to 0
copy 6 to 2
delete 6
copy 2 to 0
copy 6 to 2
delete 6

这些数字是arr 的索引。如果我们为这些对象添加一些名称,可能会清楚出了什么问题:

set 0 - A
set 1 - B
set 2 - C
set 3 - D
set 4 - E
swap 1, 4 - 1 == E, 4 == B
swap 0, 1 - 0 == E, 1 == A
copy 1 to 0 0 == A, 1 == A, pointer to E is lost
copy 4 to 1 1 == B, 4 == B
delete 4    delete B, 4 == 0, 1 still points to B
copy 2 to 0 0 == C, 2 == C, pointer to A is lost
copy 6 to 2 2 == 0
delete 6    delete null pointer, has no effect
copy 2 to 0 0 == 0, 2 == 0, pointer to C is lost
copy 6 to 2 2 == 0
delete 6    delete null pointer, has no effect
the rest just further copies around null pointers

在这个特定的示例中,它不会崩溃(至少对我而言),因为没有两次删除任何内容,但希望它清楚如何使用不同的数据发生这种情况。

大概:

    arr[n] = arr[c];    // update the old node

应该是:

    arr[c] = arr[n];    // update the old node

这会使您的代码更快崩溃,因此可能会发现更多的逻辑问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-27
    • 2011-06-06
    • 1970-01-01
    相关资源
    最近更新 更多