2014-04-25 2 views
3

Je sais qu'il ya des tonnes d'autres questions et réponses realloc et j'ai lu presque tous, mais je ne pouvais toujours pas réussi à résoudre mon problème.Comportement étrange sur Realloc: taille suivante invalide

j'ai décidé d'arrêter d'essayer quand j'ai découvert accidentaly un comportement très étrange de mon code. J'ai introduit une ligne pour essayer quelque chose, mais bien que je n'utilise pas la valeur de newElems dans main, la ligne change le comportement.

Lorsque la ligne est commenté, le code échoue au premier realloc. Y compris la ligne, le premier travail realloc. (Il se bloque toujours sur le second).

Des idées sur ce qui pourrait se passer?

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; 
} 

et la fonction Push:

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; 
} 

La fonction 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

La "partie non pertinente" que vous avez supprimée peut très bien être la plus pertinente: je suppose que c'est là que vous écrivez des choses dans 'newElems', n'est-ce pas? – dasblinkenlight

+0

Oui, et non. Je n'utilise pas newElements en dehors de cela, seulement q-> elems (qui a été assigné à newElems). Je vais l'inclure –

+1

@CiprianTomoiaga Peu importe si vous écrivez dans le bloc pointé par 'newElems' ou' q-> elems', c'est le même bloc. L'erreur est susceptible d'être là. Utilisez valdrind pour le trouver plus rapidement. – dasblinkenlight

Répondre

0

Merci à tous pour vos suggestions! Je ne l'aurais pas résolu sans eux,

Le comportement étrange a été provoqué par une erreur d'un par un. Je réallouais la file d'attente seulement quand q->size >= q->capacity, mais puisque q a été indexé de 0, cela signifiait qu'avant realloc j'écrivais dans un endroit interdit (q->elems[q->size]), qui a tout gâché.

2

realloc va changer la quantité de mémoire allouée, le cas échéant. Il est également possible de déplacer les données vers un autre endroit en mémoire si cela est plus efficace (en évitant la fragmentation de la mémoire).
La fonction renvoie alors un nouveau pointeur nouveau vers l'emplacement nouveau dans la mémoire où vos données se cachent. Vous appelez realloc et allouez (probablement) quatre fois plus de mémoire qu'avant, il est donc très probable que cette mémoire allouée se trouve ailleurs dans la mémoire.

Dans votre commentaire, vous avez dit realloc fonctionne comme free + malloc. Eh bien, dans certains cas, il peut se comporter de la même , cependant: realloc et free sont différentes fonctions, qui font des tâches différentes. Les deux sont des fonctions qui gèrent la mémoire dynamique, donc oui, évidemment il y a des similitudes, et dans le cas de realloc, ils peuvent parfois sembler être faire la même chose, cependant: As I explained here, realloc and free are fundamentally different functions

Cependant, ne pas attribuer la valeur de retour de realloc à q.elems, il vous reste un pointeur vers une adresse de mémoire qui n'est plus valide. Le reste de votre programme peut et montre probablement des signes de comportement indéfini.

Sauf si vous montrez un code plus, je soupçonne que cela prendra soin du problème:

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

Ou mieux encore, vérifiez pointeurs 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! 

En regardant votre pqueue_new fonction, je proposerais une approche différente. Renvoyez le pointeur sur Pqueue.Vous travaillez avec un morceau de mémoire dynamique, le traiter en conséquence, et que votre code reflète que tout le chemin à travers:

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; 
} 

Vous pouvez également passer la fonction d'un pointeur vers une variable de la pile:

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); 
} 

Ce seraient les approches les plus courantes.

+0

merci! Ouh, donc l'appel à 'realloc' dans la partie principale' libère 'fondamentalement les q.elems (en le déplaçant vers une nouvelle zone, pointée par 'newElems'). Je vais utiliser ce –

+0

@CiprianTomoiaga: en quelque sorte. Cela dépend vraiment, 'realloc',' free', 'malloc' et' calloc' n'allouent pas aveuglément la mémoire. Ils mappent et suivent la quantité de mémoire allouée, et gèrent la mémoire afin qu'elle ne soit pas trop fragmentée. les blocs alloués peuvent être complétés et, si vous appelez 'realloc' déplacé vers une autre section du tas, si cela signifie que la mémoire est gérée plus efficacement. Ma conjecture serait que c'est ce qui est arrivé dans votre cas –

+0

Selon la bibliothèque/OS utilisé, non, realloc * ne sera pas * gratuit * q.elems, et vous ne devriez pas avoir à réassigner 'q.elems = newElems'. Dans le cas de Linux, veuillez lire la [page de manuel] (http://linux.die.net/man/3/realloc) ("La fonction realloc() change la taille du bloc de mémoire pointé par ptr en octets de taille Le contenu sera inchangé dans la plage allant du début de la région jusqu'au minimum de l'ancienne et de la nouvelle taille. ") – Evert

Questions connexes