3
votes

l'utilisation de la fonction free () provoque une erreur d'exécution

J'ai une structure appelée Person, qui contient deux attributs - prénom et nom. Après avoir réussi l'allocation dynamique de la mémoire pour une variable de type Person, en donnant des valeurs aux attributs, je voudrais libérer de la mémoire, mais j'obtiens toujours une erreur d'exécution (la fenêtre du programme se bloque juste) voici le code:

void freePerson(Person* ps) {
    if (ps != NULL) {
        free(ps->firstName);
        free(ps->lastName);
        free(ps);
    }
}

Voici la fonction que j'utilise pour libérer la mémoire:

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

typedef struct {
char firstName[15];
char lastName[15];
} Person;

void main(){
int len = 0;
char  firstName[]="danny", lastName[]="johnes";

Person *temp = (Person*)malloc(sizeof(Person));
if (temp == NULL)
    return;

    len = strlen(firstName);
    temp->firstName[len] = (char*)malloc(sizeof(char)*(len));
    if (temp->firstName == NULL)
        return;
    strcpy(temp->firstName, firstName);

    len = strlen(lastName);
    temp->lastName[len] = (char*)malloc(sizeof(char)*(len));
    if (temp->firstName == NULL)
        return;
    strcpy(temp->lastName, lastName);

freePerson(temp);
system("pause");
return;
}

Tout ce que je veux que le code fasse - consiste à stocker le nom dans une structure allouée dynamiquement et à la libérer. Plus tard, je prévois de remplacer les noms codés en dur par des valeurs saisies à partir du fichier.

Des idées sur l'erreur? Merci.


4 commentaires

ne lancez pas malloc


Vous devez ajouter 1 pour le terminateur nul.


Pourquoi attribuez-vous à temp-> firstName [len] ?


Vous essayez de libérer des tableaux de taille fixe dans la structure Person. Ce ne sont pas des pointeurs vers des tableaux alloués dynamiquement. Ai-je tort?


4 Réponses :


1
votes

Au moins 5 numéros:

  1. Pour dupliquer une chaîne, assurez-vous que l'allocation comprend suffisamment de place pour les caractères y compris le caractère nul .

Sinon, strcpy () écrit en dehors de l'allocation qui est comportement non défini (UB).

temp->firstName = strdup(firstName);

Idem pour lastName .

  1. Attribuez également au pointeur, pas au char . @Barmar

  2. Les membres de
  3. Person sont des tableaux. Pour l'allocation dynamique, ils doivent être des pointeurs. @NthDeveloper

    // Person *temp = (Person*)malloc(sizeof(Person));
    Person *temp = malloc(sizeof *temp);
    
    // temp->firstName[len] = (char*)malloc(sizeof(char)*(len + 1));
    temp->firstName = malloc(sizeof *(temp->firstName) * (len + 1));
    

  1. Le deuxième test est faux

    // int len = 0;
    size_t len = 0;
    
  2. int contre size_t.

int len ​​= 0; suppose que la longueur de la chaîne tient dans un int . Bien que cela soit extrêmement courant, le type renvoyé par strlen () est size_t . Ce type non signé est de la bonne taille pour l'indexation et le dimensionnement des tableaux - ni trop large, ni trop étroit. Ce n'est pas un problème clé dans ce code de l'apprenant.

// if (temp->firstName == NULL)
if (temp->lastName == NULL)

Conseil: la diffusion n'est pas nécessaire. Allouez à l'objet référencé, pas au type. Plus facile à coder correctement, à réviser et à maintenir.

typedef struct {
  // char firstName[15];
  // char lastName[15];
  char *firstName;
  char *lastName;
} Person;

Conseil: Bien que ce ne soit pas le standard C, de nombreuses plates-formes fournissent strdup () aux copier des chaînes. Exemple de strdup () code .

    len = strlen(firstName);
    // temp->firstName[len] = (char*)malloc(sizeof(char)*(len ));
    temp->firstName = (char*)malloc(sizeof(char)*(len + 1));
    //                                                + 1 
    ...
    strcpy(temp->firstName, firstName);

Astuce: Probablement la plus précieuse: un bon compilateur avec des avertissements bien activés aurait dû avertir de temp-> firstName [len] = (char *) malloc (sizeof (char) * (len)) ; car il s'agit d'une erreur de type discutable dans l'affectation. Ces avertissements vous font gagner du temps, vous et nous. Assurez-vous que tous les avertissements sont activés pour votre prochaine compilation.


0 commentaires

1
votes

Vous avez déjà de l'espace alloué pour firstName, vous devez donc copier le nom dans les constraits de taille (15 octets). Vous pouvez faire cela mieux avec snprintf comme ceci:

temp->firstName = strdup(firstName); // (same for lastName)

Il en va de même pour lastName . Notez que les deux peuvent être tronqués si la longueur dépasse la taille du champ.

L'autre option est d'allouer les champs dynamiquement. Ensuite, les membres de votre structure doivent être des pointeurs, pas des tableaux de caractères:

typedef struct {
    char *firstName;
    char *lastName;
} Person;

Vous pouvez ensuite allouer et attribuer les noms comme ceci:

snprintf(temp->firstName, sizeof(temp->firstName), "%s", firstName);

Mais sachez que vous devez libérer ces champs séparément si vous souhaitez libérer l'ensemble de l'élément.


1 commentaires

Bonne utilisation de snprintf (temp-> firstName, sizeof (temp-> firstName), "% s", firstName); sur strncpy () car votre code assure temp-> firstName est un caractère nul terminé - quel que soit son état antérieur. Je m'attendrais à ce qu'un bon compilateur émette un code efficace même si le code source semble lourd. UV



1
votes

Si vous ne souhaitez pas spécifier une taille maximale pour les noms de la structure, vous devez les déclarer comme des pointeurs, pas des tableaux.

temp->firstName = malloc(strlen(firstName)+1);
if (temp->firstName == NULL) {
    return;
}
strcpy(temp->firstName, firstName);

Vous devez alors attribuer le résultat de malloc () au membre, sans l'indexer. Vous devez également ajouter 1 à strlen (firstName) , pour faire de la place pour le terminateur nul.

typedef struct {
    char *firstName;
    char *lastName;
} Person;


1 commentaires

Je pense toujours que strdup () est le moyen le plus propre et le plus lisible de dupliquer une chaîne (et fonctionne probablement encore mieux).



1
votes

Voici comment j'écrirais ceci:

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

#define FIRSTNAME_MAXLEN 15
#define LASTNAME_MAXLEN 15

typedef struct
{
        char firstName[FIRSTNAME_MAXLEN+1];
        char lastName[LASTNAME_MAXLEN+1];
} person_t;

void freePerson(person_t *ps) {
        if (ps) {
                free(ps); ps=NULL;
        }
}

int main(){
        const char *firstName="danny";
        const char *lastName="johnes";

        person_t *temp = calloc(1, sizeof(person_t));
        if (!temp) return 1;

        strncpy(temp->firstName, firstName, FIRSTNAME_MAXLEN);
        strncpy(temp->lastName, lastName, LASTNAME_MAXLEN);

        printf("test: firstname: %s\n", temp->firstName);
        printf("test: lastname: %s\n", temp->lastName);
        freePerson(temp);

        return 0;
}

Vous allouez suffisamment de place sur le tas et nettoyez les choses avec calloc (), puis vous copiez votre chaîne avec strncpy () en vous limitant aux octets réservé et évitant le débordement de tampon. À la fin, vous devez libérer () la mémoire renvoyée par calloc (). Puisque vous avez alloué char firstName [] et char lastName [] dans votre structure, vous n'avez pas besoin de réserver une autre mémoire avec malloc () pour ces membres, et vous n'avez pas non plus besoin de les libérer ().


4 commentaires

Au lieu de if (ps) {free (ps); ...; } , le code peut simplement utiliser free (ps); car free (NULL) est OK.


@chuk oui merci, mais c'est un modèle que j'utilise toujours pour éviter le double free. De cette façon, je peux appeler la fonction freePerson () plus de fois sans problèmes


@ dAm2K Pour éviter le double free, la meilleure mesure est de définir la variable du pointeur sur NULL après la libération (comme vous le suggérez). Inutile de vérifier au préalable s'il est NULL. Bien sûr, ces mesures peuvent masquer une logique défectueuse dans votre code, ce qui peut échouer sur un autre point plus tard.


@Ctx je suis totalement d'accord