【问题标题】:Program crashes at realloc on direct run, but runs fine in debug mode程序在直接运行时在 realloc 处崩溃,但在调试模式下运行良好
【发布时间】:2019-05-12 14:25:49
【问题描述】:

我正在尝试通过在插入或删除新节点时动态分配和释放内存来实现二进制堆。因此,每当调用插入/删除节点时,我都会使用 realloc 来增加/减少内存。 程序在调试模式下运行良好,但是当我直接运行它时它崩溃(可能在 realloc)

我的理由是,如果我在删除函数中删除 realloc(这意味着我永远不会释放已分配的内存),程序在直接运行时运行良好。 代码中可能存在什么问题?

P.S:我在 Windows 10 上使用 Eclipse CDT 和 Cygwin

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

typedef struct heap
{
    uint32_t size;
    int32_t* heaparray;
}heap;


void insert_max(heap** h1, int32_t value)
{
    uint32_t hole;
    heap* h = *h1;

    if(h->size == 0)
    {
        h->size = 2;
        h->heaparray = (int32_t *)(malloc(sizeof(int32_t) * h->size));
        h->heaparray[0] = 0;
        h->heaparray[1] = value;
        return;
    }

    hole = h->size++;
    h->heaparray[0] = value;
    h->heaparray = (int32_t *)(realloc(h->heaparray , sizeof(int32_t) * h->size));

    //sift up
    for(; value > h->heaparray[hole/2]; hole /= 2)
    {
        h->heaparray[hole] = h->heaparray[hole/2];
    }
    h->heaparray[hole] = value;
}

void printheap(heap* h)
{
    uint32_t index;
    printf("\nHeap: ");
    for(index = 1; index < h->size; index++)
    {
        printf("%3d\t", h->heaparray[index]);
    }
}

void siftDown_max(heap** h1, uint32_t index)
{
    uint32_t rightIndex, leftIndex, maxIndex, temp;
    heap* h = *h1;
    leftIndex = (2 * index);
    rightIndex = (2 * index) + 1;
    if(rightIndex >= h->size)
    {
        if(leftIndex >= h->size)
            return;
        else
        {
            maxIndex = leftIndex;
        }
    }
    else
    {
        if(h->heaparray[rightIndex] >= h->heaparray[leftIndex])
        {
            maxIndex = rightIndex;
        }
        else
        {
            maxIndex = leftIndex;
        }
    }
    if(h->heaparray[index] < h->heaparray[maxIndex])
    {
        temp = h->heaparray[index];
        h->heaparray[index] = h->heaparray[maxIndex];
        h->heaparray[maxIndex] = temp;
        siftDown_max(h1, maxIndex);
    }
}

void siftDown_min(heap** h1, uint32_t index)
{
    uint32_t rightIndex, leftIndex, minIndex, temp;
    heap* h = *h1;
    leftIndex = 2 * index;
    rightIndex = (2 * index) + 1;

    if(rightIndex >= h->size)
    {
        if(leftIndex >= h->size)
        {
            return;
        }
        else
        {
            minIndex = leftIndex;
        }
    }
    else
    {
        if(h->heaparray[leftIndex] <= h->heaparray[rightIndex])
        {
            minIndex = leftIndex;
        }
        else
        {
            minIndex = rightIndex;
        }
    }

    if(h->heaparray[index] > h->heaparray[minIndex])
    {
        temp = h->heaparray[minIndex];
        h->heaparray[minIndex] = h->heaparray[index];
        h->heaparray[index] = temp;
        siftDown_min(h1, minIndex);
    }
}

void Delete(heap** h1, bool maxflag)
{
    uint32_t hole = 0;
    heap* h = *h1;
    if(h->size == 1)
    {
        h = NULL;
        return;
    }
    else
    {
        hole = --h->size;
        h->heaparray[1] = h->heaparray[hole];
        h->heaparray = (int32_t *)(realloc(h->heaparray , sizeof(int32_t) * h->size));
        if(maxflag)
        {
            siftDown_max(h1, 1);
        }
        else
        {
            siftDown_min(h1, 1);
        }
    }
}

void insert_min(heap** h1, int32_t value)
{
    uint32_t hole_index = 0;
    heap* local_heap = *h1;
    if (local_heap->size == 0)
    {
        local_heap->size = 2;
        local_heap->heaparray = (int32_t*)malloc(sizeof(int32_t) * local_heap->size);
        local_heap->heaparray[0] = 0;
        local_heap->heaparray[1] = value;
        return;
    }
    hole_index = local_heap->size++;
    local_heap->heaparray[0] = value;

    for(; value < local_heap->heaparray[hole_index/2]; hole_index /= 2)
    {
        local_heap->heaparray[hole_index] = local_heap->heaparray[hole_index / 2];
    }

    local_heap->heaparray[hole_index] = value;

}


int main(void)
{

    int hy = 0;
    heap *newheap = (heap *)(malloc(sizeof(heap)));
    newheap->size = 0;
    insert_max(&newheap, 5);
    insert_max(&newheap, 3);
    insert_max(&newheap, 8);
    insert_max(&newheap, 2);
    insert_max(&newheap, 10);
    insert_max(&newheap, 13);
    insert_max(&newheap, 7);
    insert_max(&newheap, 26);
    insert_max(&newheap, 11);
    printheap(newheap);
    Delete(&newheap, true);
    printheap(newheap);
    Delete(&newheap, true);
    printheap(newheap);
    Delete(&newheap, true);
    printheap(newheap);
    Delete(&newheap, true);
    printheap(newheap);
    Delete(&newheap, true);
    printheap(newheap);
    Delete(&newheap, true);
    printheap(newheap);
    Delete(&newheap, true);
    printheap(newheap);
    Delete(&newheap, true);
    printheap(newheap);
    Delete(&newheap, true);
    printheap(newheap);
    insert_max(&newheap, 134);
    printheap(newheap);

    heap *minheap = (heap *)(malloc(sizeof(heap)));
    minheap->size = 0;
    insert_min(&minheap, 5);
    printheap(minheap);
    insert_min(&minheap, 3);
    printheap(minheap);
    insert_min(&minheap, 8);
    printheap(minheap);
    insert_min(&minheap, 2);
    printheap(minheap);
    insert_min(&minheap, 10);
    printheap(minheap);
    insert_min(&minheap, 13);
    printheap(minheap);
    insert_min(&minheap, 7);
    printheap(minheap);
    insert_min(&minheap, 26);
    printheap(minheap);
    insert_min(&minheap, 11);
    printheap(minheap);
    Delete(&minheap, false);
    printheap(minheap);
    Delete(&minheap, false);
    printheap(minheap);
    Delete(&minheap, false);
    printheap(minheap);
    Delete(&minheap, false);
    printheap(minheap);
    Delete(&minheap, false);
    printheap(minheap);
    Delete(&minheap, false);
    printheap(minheap);
    Delete(&minheap, false);
    printheap(minheap);
    Delete(&minheap, false);
    printheap(minheap);
    Delete(&minheap, false);
    printheap(minheap);
    insert_min(&minheap, 138);
    printheap(minheap);
    hy = 3;

    return EXIT_SUCCESS;
}

【问题讨论】:

  • 先阅读this
  • Delete(&amp;newheap, true); printheap(newheap);Delete(&amp;newheap, true); printheap(newheap); 看起来很可疑......

标签: c heap-memory realloc


【解决方案1】:

我为你做了一个Minimal, Complete, and Verifiable example,那么很容易发现一个严重的错误。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

typedef struct heap
{
    uint32_t size;
    int32_t* heaparray;
    // START DEBUG CODE    
    uint32_t debug_allocated_size;   // contains the actual allocated size
    // END DEBUG CODE
}heap;


void insert_min(heap** h1, int32_t value)
{
    uint32_t hole_index = 0;
    heap* local_heap = *h1;
    if (local_heap->size == 0)
    {
        local_heap->size = 2;
        local_heap->heaparray = (int32_t*)malloc(sizeof(int32_t) * local_heap->size);

        // START DEBUG CODE
        local_heap->debug_allocated_size = local_heap->size;
        // END DEBUG CODE

        local_heap->heaparray[0] = 0;
        local_heap->heaparray[1] = value;
        return;
    }
    hole_index = local_heap->size++;
    local_heap->heaparray[0] = value;

    for(; value < local_heap->heaparray[hole_index/2]; hole_index /= 2)
    {
      // START DEBUG CODE
      if (local_heap->debug_allocated_size >= hole_index)
      {
        // if hole_index is larger than the actuallly allocated size there is a problem...
        printf("argh: buffer overflow\n");
        exit(1);
      }
      // END DEBUG CODE

      local_heap->heaparray[hole_index] = local_heap->heaparray[hole_index / 2];
    }

    local_heap->heaparray[hole_index] = value;
}


int main(void)
{
    heap *minheap = (heap *)(malloc(sizeof(heap)));
    minheap->size = 0;
    insert_min(&minheap, 5);
    insert_min(&minheap, 3);
    insert_min(&minheap, 8);
    insert_min(&minheap, 2);

    return EXIT_SUCCESS;
}

看看我添加的 cmets。这应该可以帮助您纠正错误。

免责声明:您的代码的其他部分可能存在更多错误。

期待您的下一个问题为什么我的代码在调试模式下运行良好

答案:您的程序表现出“未定义的行为”。一旦你覆盖了不属于你的记忆,你就进入了“未定义行为”的领域,从那时起任何事情都可能发生。

【讨论】:

    【解决方案2】:

    您在使用realloc 时存在潜在错误:

    h->heaparray = (int32_t *)(realloc(h->heaparray , sizeof(int32_t) * h->size));
    

    如果realloc 因任何原因失败,它将返回NULL。发生这种情况时,您的程序将严重崩溃。 realloc 只是一个丑陋的函数,你应该非常小心地使用它。

    我没有解决您的问题的方法。不过,我确实有一些关于构建堆的一般建议,以及一般可调整大小的集合数据结构。

    通过在每次插入和删除时重新分配,您已经创建了一个具有 O(n) 插入和 O(n) 删除的堆。您不妨使用无序数组,因为堆结构的好处被每次重新分配和复制内存的成本所掩盖。

    如果您想使用动态数组,您应该从一些最小大小开始,比如 16 个项目,并跟踪数组中的可用空间。当你重新分配时,增加超过 1。可能你最好的选择是将数组的大小加倍。这样,您就可以摊销重新分配的成本。您的插入和删除变为 O(log n) 而不是 O(n)。

    关键是您的heap 结构需要一个count 字段来跟踪堆中的当前项数:

    typedef struct heap
    {
        uint32_t size;  /* size of the heap array */
        uint32_t count; /* number of items currently in the heap */
        int32_t* heaparray;
    }heap;
    

    所以当你插入时,你检查是否count == size。如果是这样,则重新分配以使大小加倍。插入和删除时,请始终在计算中使用count(而不是size,如您当前的代码)。

    删除项目时,仅在size &gt; count*2 时重新分配。这样,您可以最大限度地减少对realloc 的调用。如果您想最小化堆占用的空间量,您可能还需要一个可以调用的 trimToCount 函数。

    另外,请重新考虑您对基于 1 的堆的选择。 C 数组是从 0 开始的,因此堆的根位于索引 0 处是有意义的。调整父子计算以便它们与从 0 开始的堆一起工作是很简单的。有关此处推理的更多信息,请参阅 https://stackoverflow.com/a/49806133/56778http://blog.mischel.com/2016/09/19/but-thats-the-way-weve-always-done-it/

    【讨论】:

    • 这是实现堆的所有好建议,但我认为这与程序崩溃没有任何关系。
    • 谢谢你,这是有道理的,但为什么 realloc 在这里失败了?我只是想重新分配最大 200 字节
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-26
    • 2013-07-27
    相关资源
    最近更新 更多