Realloc上的奇怪行为:下一个大小无效

时间:2014-04-25 14:10:26

标签: c realloc

我知道还有很多其他的realloc问题和答案,而且我已经阅读了几乎所有这些问题和答案,但我仍无法解决我的问题。

当我偶然发现我的代码非常奇怪的行为时,我决定停止尝试。 我引入了一行来尝试一些东西,但是虽然我没有在main中使用newElems的值,但该行改变了行为。

当评论该行时,代码首先失败realloc。包括该行,第一个realloc工作。 (它仍然在第二个崩溃。)

关于可能发生的事情的任何想法?

int main(int argc, char** argv) {
    Pqueue q = pqueue_new(3);
    Node a = {.name = "a"}, b = {.name = "b"},
         c = {.name = "c"}, d = {.name = "d"};

    push(& q, & a, 3);
    // the next one is the strange line: as you can see, it doesn't modify q
    // but commenting it out produces different behaviour
    Pqueue_elem* newElems = realloc(q.elems, 4 * q.capacity * sizeof *newElems);
    push(& q, & b, 5);
    push(& q, & c, 4);

    char s[5];
    Node* n;
    for (int i = 1; i <= 65; ++i) {
        sprintf(s, "%d", i);
        n = malloc(sizeof *n);
        n->name = strdup(s);
        push(& q, n, i);
    }

    Node* current = NULL;
    while ((current = pop(& q))) {
        printf("%s ", current->name);
    }
    return 0;
}

和推送功能:

void push(Pqueue* q, Node* item, int priority) {
    if (q->size >= q->capacity) {
        if (DEBUG)
            fprintf(stderr, "Reallocating bigger queue from capacity %d\n",
                    q->capacity);
        q->capacity *= 2;
        Pqueue_elem* newElems = realloc(q->elems,
                                        q->capacity * sizeof *newElems);
        check(newElems, "a bigger elems array");
        q->elems = newElems;
    }

    // append at the end, then find its correct place and move it there
    int idx = ++q->size, p;
    while ((p = PARENT(idx)) && priority > q->elems[p].priority) {
        q->elems[idx] = q->elems[p];
        idx = p;
    }
    // after exiting the while, idx is at the right place for the element
    q->elems[idx].data = item;
    q->elems[idx].priority = priority;
}

pqueue_new函数:

Pqueue pqueue_new(unsigned int size) {
    if (size < 4)
        size = 4;
    Pqueue* q = malloc(sizeof *q);
    check(q, "a new queue.");
    q->capacity = size;
    q->elems = malloc(q->capacity * sizeof *(q->elems));
    check(q->elems, "queue's elements");

    return *q;
}

2 个答案:

答案 0 :(得分:2)

如果需要,

realloc将更改分配的内存量。如果效率更高(避免内存碎片),也可以将数据自由移动到内存中的其他位置。
然后,该函数将一个 new 指针返回到数据隐藏的内存中的 new 位置。您正在调用realloc,并且(可能)分配的内存是以前的四倍,因此分配的内存很可能位于内存的其他位置。

在评论中,您说realloc的作用类似于free + malloc。好吧,在某些情况下,可以表现得类似,但是:reallocfree是不同的功能,执行不同的任务。两者都是管理动态内存的函数,所以是的,显然有相似之处,在realloc的情况下,有时他们似乎也在做同样的事情,但是:As I explained here, realloc and free are fundamentally different functions

但是,如果不将realloc的返回值分配给q.elems,则会留下指向不再有效的内存地址的指针。然后,你的程序的其余部分可能并且可能确实表现出未定义行为的迹象。

除非你展示更多代码,否则我怀疑这会解决问题:

//change:
Pqueue_elem* newElems = realloc(q.elems, 4 * q.capacity * sizeof *newElems);
//to
q.elems = realloc(q.elems, 4 * q.capacity * sizeof *newElems);

或者更好的是,检查NULL指针:

Pqueue_elem* newElems = realloc(q.elems, 4 * q.capacity * sizeof *newElems);
if (newElems == NULL)
    exit( EXIT_FAILURE );// + fprintf(stderr, "Fatal error...");
q.elems = newElems;//<-- assign new pointer!

查看您的pqueue_new函数,我建议采用不同的方法。让它返回指向Pqueue的指针。您正在处理一段动态内存,相应地对待它,并让您的代码一直反映出来:

Pqueue * pqueue_new(size_t size)
{//size_t makes more sense
    if (size < 4)
        size = 4;
    Pqueue* q = malloc(sizeof *q);
    check(q, "a new queue.");
    q->capacity = size;
    q->elems = malloc(q->capacity * sizeof *(q->elems));
    check(q->elems, "queue's elements");

    return q;
}

或者,将函数传递给堆栈变量:

void pqueue_new(Pqueue *q, size_t size)
{
    if (q == NULL)
    {
        fprintf(stderr, "pqueue_new does not do NULL pointers, I'm not Chuck Norris");
        return;//or exit
    }
    if (size < 4)
        size = 4;
    check(q, "a new queue.");
    q->capacity = size;
    q->elems = malloc(q->capacity * sizeof *(q->elems));
    check(q->elems, "queue's elements");
}
//call like so:
int main ( void )
{
    Pqueue q;
    pqueue_new(&q, 3);
}

那些将是更常见的方法。

答案 1 :(得分:0)

谢谢大家的建议!没有它我就不会解决它,

奇怪的行为是由一个错误造成的。我只在q->size >= q->capacity时重新分配队列,但由于q从0开始索引,这意味着在realloc之前我在禁止的位置(q->elems[q->size])写入,这使得一切都搞砸了。