2014-04-25 37 views
3

我知道还有其他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; 
} 
+0

你删除的“不相关部分”可能是最相关的:我认为这是你写入'newElems'的东西,对吗? – dasblinkenlight

+0

是的,没有。 我不使用newElements,如果只有q-> elems(已分配给newElems)。我将包括它 –

+1

@CiprianTomoiaga如果你写入'newElems'或者'q-> elems'指向的块,它就是同一个块。错误可能在那里。使用valdrind更快地找到它。 – dasblinkenlight

回答

0

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

奇怪的行为是由一个错误导致的。我仅在q->size >= q->capacity时重新分配队列,但由于q从0开始索引,这意味着我在禁止位置(q->elems[q->size])写入realloc之前,这会将所有内容搞砸。

2

realloc将改变被分配,如果需要的存储器的量。如果数据更有效(避免内存碎片),将数据移动到内存中的另一个地方也是免费的。
的功能,然后,返回指针位置在内存中数据被隐藏。你打电话realloc,并且分配(可能)4倍于以前的内存,所以很可能分配的内存位于内存中的其他地方。

在您的评论中,你说realloc的作品类似free + malloc。那么,在某些情况下,其的行为可能类似,但是:reallocfree是不同的功能,它们执行不同的任务。无论是管理动态内存,所以是的,很显然有相似之处的功能,并在realloc的情况下,有时他们似乎可以做同样的事情,但是:As I explained here, realloc and free are fundamentally different functions

然而,通过不分配返回值reallocq.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); 
} 

这些将是更常见的方法。

+0

谢谢! Ouh,所以主要调用'realloc'基本上'free's q.elems(通过将它移动到一个新区域,由'newElems'指向)。我会用那个 –

+0

@CiprianTomoiaga:有点。这真的取决于'realloc','free','malloc'和'calloc'不只是盲目地分配内存。他们映射并跟踪分配的内存量,并管理内存,使其不会变得过于分散。可以填充已分配的块,如果将'realloc'移动到堆的另一个部分,这意味着内存管理更有效。我的猜测是,这是你的情况发生了什么 –

+0

根据使用的库/操作系统,不,realloc将*不*免费q。elems,你不应该重新分配'q.elems = newElems'。对于Linux,请阅读[手册页](http://linux.die.net/man/3/realloc)(“realloc()函数将ptr指向的内存块的大小更改为size字节。从区域的开始到旧的和新的大小的最小值范围内的内容将保持不变“) – Evert

相关问题