web-dev-qa-db-fra.com

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

J'essaie d'apprendre le C et j'essaie actuellement d'écrire une structure de données de pile de base, mais je n'arrive pas à obtenir le droit de base de malloc/free.

Voici le code que j'ai utilisé (je poste juste une petite partie ici pour illustrer un problème spécifique, pas le code total, mais le message d'erreur a été généré simplement en exécutant cet exemple de code dans 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;
}

Lorsque je le passe par 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 la fonction destroyEntry n'est pas autorisée à modifier la mémoire allouée explicitement dans main. Est-ce correct? Pourquoi ne puis-je pas simplement free la mémoire que j'ai allouée dans main dans une autre fonction? (et ce comportement est-il spécifique à main?)

65
Jeff Tratner

Chaque fois que vous passez 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 d'origine, ce qui n'a aucun sens.

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

51

Cela passe par valeur, ce qui signifie que la copie est créée, vous essayez donc de libérer la mémoire, où réside la variable locale entry. Notez que entry est un objet avec une durée de stockage automatique et la mémoire où il réside sera automatiquement libérée lorsque votre programme sortira du champ d'application de la fonction destroyEntry.

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

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

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

Ensuite, au lieu de destroyEntry(*(Apple));, vous appelez simplement destroyEntry(Apple);. Notez que s'il n'y a aucune autre fonctionnalité connectée à la fonction destroyEntry, elle est redondante et il vaut mieux appeler directement free(Apple).

37
LihO

Les autres réponses ici soulignent le problème principal - parce que vous déréférencez votre Apple lorsque vous appelez destroyEntry dans main (), il passe par référence, créant une copie.

Même une fois que vous connaissez votre problème, il est utile de revenir à l'erreur et d'essayer de connecter le texte de ce que vous voyez au problème, afin que la prochaine fois qu'il apparaisse, vous soyez plus susceptible de le résoudre rapidement. Je trouve que les erreurs C et C++ peuvent parfois sembler d'une ambiguïté affolante.

En règle générale, lorsque je rencontre des problèmes pour libérer des pointeurs ou supprimer des objets, j'aime imprimer les adresses, en particulier lorsque je les attribue et lorsque j'essaye de les libérer. valgrind vous a déjà donné l'adresse du mauvais pointeur, mais cela permet de la comparer à une bonne.

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 cela, vous remarquerez que l'instruction printf () vous a donné une adresse quelque chose comme 0x8024712 (juste en créant une adresse dans la bonne plage générale), mais votre sortie valgrind a donné 0x4028E58. Vous remarquerez qu'ils sont à deux endroits très différents (en fait, "0x4 ..." est sur la pile, pas le tas d'où malloc () alloue, mais je suppose que si vous débutez c'est pas encore un drapeau rouge pour vous), donc vous savez que vous essayez de libérer de la mémoire au mauvais endroit, d'où "free free ()" invalide.

Donc, à partir de là, vous pouvez vous dire "D'accord, mon pointeur est en quelque sorte corrompu." Vous avez déjà réduit votre problème à un petit exemple compilable, il ne vous faudra donc pas longtemps pour le résoudre à partir de là.

TL; DR - lorsque vous rencontrez des erreurs liées au pointeur, essayez d'imprimer les adresses ou de les trouver dans votre débogueur préféré. Cela vous indique souvent au moins dans la bonne direction.

Bien sûr, rien de tout cela ne décourage la publication de votre question sur Stack Exchange. Des centaines de programmeurs bénéficieront probablement de votre action.

9
gkimsey