65 votes

Mémoire libre allouée dans une fonction différente?

Je suis en train d'apprendre le C et je suis en train d'essayer d'écrire une base de données de pile de la structure, mais je n'arrive pas à obtenir de base malloc/free droit.

Voici le code que j'ai utilisé (je suis en train de poster une petite partie ici pour illustrer un problème spécifique, et non pas la totalité du code, mais le message d'erreur a été générée par l'exécution de cet exemple de code en valgrind)

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

typedef struct Entry {
    struct Entry *previous;
    int value;
} Entry;

void destroyEntry(Entry entry);

int main(int argc, char *argv[])
{
    Entry* apple;
    apple = malloc(sizeof(Entry));
    destroyEntry(*(apple));
    return 0;
}

void destroyEntry(Entry entry)
{
    Entry *entry_ptr = &entry;
    free(entry_ptr);
    return;
}

Quand je le lance via valgrind avec --leak-check=full --track-origins=yes, j'obtiens l'erreur suivante:

==20674== Invalid free() / delete / delete[] / realloc()
==20674==    at 0x4028E58: free (vg_replace_malloc.c:427)
==20674==    by 0x80485B2: destroyEntry (testing.c:53)
==20674==    by 0x8048477: main (testing.c:26)
==20674==  Address 0xbecc0070 is on thread 1's stack

Je pense que cette erreur signifie que l' destroyEntry fonction n'est pas autorisé à modifier la mémoire allouée de manière explicite dans le main. Est ce que le droit? Pourquoi ne puis-je pas juste free la mémoire, je alloué en main dans une autre fonction? (et est-ce le comportement d'une certaine manière spécifique à la main?)

51voto

Oli Charlesworth Points 148744

Chaque fois que vous transmettez un paramètre à une fonction, une copie est effectuée et la fonction fonctionne sur cette copie. Donc, dans votre cas, vous essayez de free une copie de l’objet original, ce qui n’a aucun sens.

Vous devez modifier votre fonction pour prendre un pointeur, puis vous pouvez l'appeler free directement sur ce pointeur.

38voto

LihO Points 21648

C'est le passage par valeur, ce qui signifie que la copie est créée, donc vous essayez de libérer de la mémoire, où la variable locale entry réside. Notez que entry est un objet automatique de la durée de stockage et de mémoire où il réside sera libéré automatiquement lorsque votre programme est hors de portée de l' destroyEntry fonction.

void destroyEntry(Entry entry)
{
    Entry *entry_ptr = &entry;
    free(entry_ptr);
    return;
}

Votre fonction doit prendre un pointeur (passage par référence):

void destroyEntry(Entry *entry)
{
    free(entry);
}

Alors au lieu de destroyEntry(*(apple)); vous appelez destroyEntry(apple);. Notez que si il n'y a aucune autre fonctionnalité connecté avec destroyEntry de la fonction, c'est redondant et il est préférable de simplement appeler directement free(apple).

9voto

gkimsey Points 321

Les autres réponses ici souligner le principal problème -- parce que vous déréférencement de votre apple lorsque vous appelez destroyEntry dans main(), il passe par référence, la création d'une copie.

Même une fois que vous connaissez votre problème, il permet de revenir à l'erreur et essayez de vous connecter le texte de ce que vous voyez le problème, de sorte que la prochaine fois qu'il arrive, vous pourriez être plus susceptible de le comprendre rapidement. Je trouve le C et le C++ erreurs peuvent sembler maddeningly ambigu parfois.

En général, quand je vais avoir de la difficulté à libérer les pointeurs ou la suppression d'objets, j'aime à imprimer les adresses, surtout quand je les répartir et à droite lorsque j'essaie de le libérer. valgrind vous ai déjà donné l'adresse du pointeur incorrect, mais il est utile de le comparer à un bon.

int main()
{
  Entry * apple;
  apple = malloc(sizeof(Entry));
  printf("apple's address = %p", apple);  // Prints the address of 'apple'
  free(apple);   // You know this will work
}

Après avoir fait cela, vous remarqueriez le printf() vous a donné l'adresse de quelque chose comme 0x8024712 (juste une adresse dans le droit général de gamme), mais votre valgrind sortie a donné 0x4028E58. Vous remarqueriez qu'ils sont dans deux endroits très différents (en fait, "0x4..." est sur la pile, pas le tas où malloc() alloue, mais je suppose que si vous êtes juste de commencer ce n'est pas un drapeau rouge pour vous encore), de sorte que vous savez que vous essayez de libérer de la mémoire dans le mauvais endroit, donc non valide "free()".

Donc à partir de là, vous pouvez dire à vous-même "d'Accord, d'une certaine manière mon pointeur est corrompu." Vous avez déjà bouilli vers le bas de votre problème à un petit compilable exemple, alors il ne vous faudra pas longtemps pour le résoudre, à partir de là.

TL;DR - lors de l'exécution en pointeur liées à des erreurs, essayez d'imprimer les adresses ou les trouver dans vos favoris débogueur. Il est souvent à moins de points dans la bonne direction.

Rien de tout cela est de décourager de poster votre question sur Stack Exchange, bien sûr. Des centaines de programmeurs pourront tirer avantage de votre avoir fait.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X