【问题标题】:Realloc manipulating data out of its scopeRealloc 操作超出其范围的数据
【发布时间】:2019-03-28 13:41:11
【问题描述】:

我的 C 代码出现了非常不寻常的行为。我正在实现一个最小和最大堆,如果堆容量达到,它应该动态改变大小。问题是当我调用realloc 来增加堆的元素数组时,它以以下方式运行(假设两个堆都处于最大容量):

  1. 如果我只在其中一个堆中添加一个新元素,那么重新分配就可以完美地工作。

  2. 如果我在两个堆中添加一个新元素(一个接一个),第二个会完美地重新分配,但第一个的数据会因一些垃圾值和一些零而损坏。

请看下面的相关功能。 (问题发生在 main 函数的第 8 行和第 9 行)。

我不明白为什么在不同的堆上调用insert 函数会改变前一个堆的值。

我不知道是 realloc 功能搞砸了还是我的打印功能搞砸了。感谢您的帮助。

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

#define leftChild(x) (x << 1)
#define rightChild(x) ((x << 1) + 1)
#define parent(x) (x >> 1)


typedef int T;

typedef struct {
    int size;
    int capacity;
    T *elements;
}   Heap;

void swap(Heap *heap, int a, int b) {
    T temp = heap->elements[a];
    heap->elements[a] = heap->elements[b];
    heap->elements[b] = temp;
}

Heap *newHeap(int capacity) {
    Heap *heap = malloc(sizeof(Heap));
    heap->capacity = capacity;
    heap->size = 0;
    heap->elements = malloc(sizeof(T) * (capacity + 1));
    return heap;
}

void increaseKey(Heap *heap, int i, T key) {
    heap->elements[i] = key;
    while (i > 1 && heap->elements[parent(i)] < heap->elements[i]) {
        swap(heap, parent(i), i);
        i = parent(i);
    }
}

void decreaseKey(Heap *heap, int i, T key) {
    heap->elements[i] = key;
    while (i > 1 && heap->elements[parent(i)] > heap->elements[i]) {
        swap(heap, parent(i), i);
        i = parent(i);
    }
}

void insert(Heap *heap, T key, bool isMinHeap) {
    if (heap->size >= heap->capacity) {
        heap->elements = realloc(heap->elements, heap->capacity * 2);
        heap->capacity = heap->capacity * 2;
    }
    heap->size++;
    heap->elements[heap->size] = 0;
    if (isMinHeap) decreaseKey(heap, heap->size, key);
    else increaseKey(heap, heap->size, key);
}

void printHeap(Heap *heap) {
    int i;
    printf("[");
    for (i = 1; i < heap->size; i++) {
        printf("%d,", heap->elements[i]);
    }
    if (heap->size != 0) {
        printf("%d", heap->elements[heap->size]);
    }
    printf("]\n");
}

int main(void) {
    Heap *minHeap = newHeap(5);
    Heap *maxHeap = newHeap(5);
    for (int i = 0; i < 5; i++) {
        insert(minHeap, i, true);
        insert(maxHeap, i, false);
    }
    printf("now start\n");
    insert(minHeap, 10, true);
    insert(maxHeap, 10, false);
    printHeap(minHeap);
    printHeap(maxHeap);
}

【问题讨论】:

  • parent(i) 中的decreaseKey() 是什么?
  • 什么是swap()parent()?您发布了这么多代码,再多 3 行添加 #includes 不会有什么不同。
  • 我不知道是 realloc 功能出了问题还是我的打印功能出了问题。 你的操作系统是什么?视窗? Linux?您可能使用的realloc() 的副本有多少亿(如果不是数十亿)正被整个地球使用?在您发现如此广泛使用的函数实现中的错误之前,宇宙的热寂更有可能发生,该函数的使用量与realloc() 一样多。
  • 您经常访问heap-&gt;elements[heap-&gt;size],这是超出范围的。 (是的,我知道您为一个插槽分配了内存只是为了确定,但感觉(并且可能是)错误的。)哦,您需要在 realloc 中使用 sizeof(T),就像使用 malloc 一样。
  • @mch 抱歉,我以为我包括了所有内容。请在最近的编辑中找到最新的代码。

标签: c pointers data-structures realloc


【解决方案1】:

您的(否则非常整洁的)程序中有两个主要错误:

首先,您必须为mallocrealloc 提供一个以字节为单位的大小,这意味着除非您分配char 的数组,否则在某处应该有一个sizeof(T)。您在分配初始数组时会这样做,但在重新分配时忘记了。

其次,您使用从 1 开始的索引来访问数组。在 C 中,数组是从零开始的。这也适用于在堆上分配的数据。第一个索引是0,最后一个有效索引是heap-&gt;size - 1

这意味着当你追加一个元素时,你使用当前大小作为插入索引,然后增加大小。 (当然你必须先检查 tere 是否是空间,但你要这样做。)所以:

// ... allocate if necessary ...

heap->elements[heap->size] = 0;
if (isMinHeap) decreaseKey(heap, heap->size, key);
else increaseKey(heap, heap->size, key);

heap->size++;

这是一种常见的模式,在将内容附加到数组时经常看到:array[size++] = stuff;

最后,您可能必须更新确定父子节点的函数:

parent(n) == (n - 1) / 2;
left(n) == 2*n + 1;
right(n) == 2*n + 2;

用完后别忘了free你的记忆。

【讨论】:

  • realloc 调用更改为包含sizeof(T) 对我有用。我尝试了使用基于 1 的索引的解决方案,它仍然有效(我知道它不是 C 方式,但它使位移保持良好和整洁)。非常感谢你的帮助。如果只有一个插入调用(我的问题中的案例 1),我仍然不明白为什么程序可以工作。无论如何,非常感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-04-28
  • 2018-07-18
  • 1970-01-01
  • 2015-05-18
  • 1970-01-01
  • 2023-03-27
  • 1970-01-01
相关资源
最近更新 更多