IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

Erreur de segmentation en utilisant la fonction free


Sujet :

C

  1. #1
    Membre du Club
    Homme Profil pro
    Statistique
    Inscrit en
    Octobre 2014
    Messages
    33
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Jura (Franche Comté)

    Informations professionnelles :
    Activité : Statistique

    Informations forums :
    Inscription : Octobre 2014
    Messages : 33
    Points : 48
    Points
    48
    Par défaut Erreur de segmentation en utilisant la fonction free
    Bonjour.

    J’ai une erreur de segmentation en utilisant la fonction free que je ne parviens pas à comprendre.
    L’erreur disparaît si je supprime l’instruction free(self->states);
    J’ai épuré le code pour ne garder que les éléments liés à l’erreur.

    Mon output est précisément
    Coucou 1
    Coucou 2
    Erreur de segmentation (core dumped)

    Quelqu’un comprend-t-il mon erreur?

    Merci.


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    //#include "fa.h"
     
     
    struct fa
    {
    	struct state *states; //Tableau des états composant l'automate	
    };
     
     
    struct state
    {
    	//Chaque état, peut être final et/ou initial
    	bool is_initial;
    	bool is_final;
    };
     
     
    void fa_create(struct fa *self, size_t alpha_count, size_t state_count)
    {
    	self = malloc(sizeof(struct fa*));
    	self->states = calloc(state_count, sizeof(struct state*));	
    }
     
     
    void fa_destroy(struct fa *self)
    {
    	free(self->states); // L'erreur disparait si je commente cela...
    	free(self);
    }
     
     
    int main()
    {
    struct fa *autom=NULL;
    printf("Coucou 1 \n");
    fa_create(autom, 3, 5);
    printf("Coucou 2 \n");
    fa_destroy(autom);
    printf("Coucou 3 \n");
    }

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 368
    Points : 23 620
    Points
    23 620
    Par défaut
    Bonjour,

    Ici :

    Citation Envoyé par Obofix le gaulois Voir le message

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void fa_create(struct fa *self, size_t alpha_count, size_t state_count)
    {
    	self = malloc(sizeof(struct fa*));
    	self->states = calloc(state_count, sizeof(struct state*));	
    }
    Tu alloues suffisamment d'espace non pas pour une structure fa (reférencée par un « pointeur sur une structure fa » que tu as ici nommé self) mais pour un « pointeur sur une structure fa », soit 4 ou 8 octets uniquement.

    Quand tu l'exploites, tu fais un dépassement de segment mais comme tout cela reste à l'intérieur du segment alloué à ton processus, le micro-processeur ne déclenche pas de segfault. Il reste que tu corromps sans le savoir tes données adjacentes, et le crash se produit a posteriori, au moment de libérer la mémoire.

  3. #3
    Membre du Club
    Homme Profil pro
    Statistique
    Inscrit en
    Octobre 2014
    Messages
    33
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Jura (Franche Comté)

    Informations professionnelles :
    Activité : Statistique

    Informations forums :
    Inscription : Octobre 2014
    Messages : 33
    Points : 48
    Points
    48
    Par défaut
    Effectivement vous avez raison.

    En revanche je dois avoir une autre erreur car même en retirant l’étoile de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    self = malloc(sizeof(struct fa*));
    j’ai de toujours une erreur de segmentation...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    //#include "fa.h"
     
    struct fa
    {
    	struct state *states; //Tableau des états composant l'automate	
    };
     
     
    struct state
    {
    	//Chaque état, peut être final et/ou initial
    	bool is_initial;
    	bool is_final;
    };
     
     
    void fa_create(struct fa *self, size_t alpha_count, size_t state_count)
    {
    	self = malloc(sizeof(struct fa));
    	self->states = calloc(state_count, sizeof(struct state*));	
    }
     
     
    void fa_destroy(struct fa *self)
    {
    	free(self->states); // L'erreur disparait si je commente cela...
    	free(self);
    }
     
     
     
    int main()
    {
    struct fa *autom=NULL;
    printf("Coucou 1 \n");
    fa_create(autom, 3, 5);
    printf("Coucou 2 \n");
    fa_destroy(autom);
    printf("Coucou 3 \n");
    }

  4. #4
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Bonjour,
    Il y a deux problèmes.
    1. Même erreur sur states.
      Sinon plutôt que faire un sizeof du type, tu peux utiliser l'écriture alternative "sizeof du type pointé":
      Code C : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      void fa_create(struct fa *self, size_t alpha_count, size_t state_count)
      {
      	self = malloc(sizeof(*self));
      	self->states = calloc(state_count, sizeof(self->states[0]));	
      }
    2. Ton appel de fonction est incorrect: Ta fonction est censée modifier le pointeur self, mais ne reçoit en paramètre qu'une copie de celle-ci. Pour corriger ça, soit tu retourne la variable:
      Code C : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      struct fa * fa_create(size_t alpha_count, size_t state_count)
      {
      	struct fa * self = malloc(sizeof(*self));
      	self->states = calloc(state_count, sizeof(self->states[0]));	
      	return self;
      }
      Soit tu utilises un pointeur de pointeur:
      Code C : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      void fa_create(struct fa **ppself, size_t alpha_count, size_t state_count)
      {
      	struct fa *self = malloc(sizeof(*self));
      	*self->states = calloc(state_count, sizeof(self->states[0]));	
      	*ppself = self;
      }
      N'oublie pas de modifier l'appel en conséquence.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  5. #5
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 678
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 678
    Points : 30 965
    Points
    30 965
    Billets dans le blog
    1
    Par défaut
    Bonjour
    Citation Envoyé par Médinoc Voir le message
    Pour corriger ça, soit tu retourne la variable:
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    void fa_create(size_t alpha_count, size_t state_count)
    {
    	struct fa * self = malloc(sizeof(*self));
    	self->states = calloc(state_count, sizeof(self->states[0]));	
    	return self;
    }
    et ne pas oublier de faire correspondre le type de la fonction en conséquence...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  6. #6
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    OK, c'est corrigé.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  7. #7
    Membre du Club
    Homme Profil pro
    Statistique
    Inscrit en
    Octobre 2014
    Messages
    33
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Jura (Franche Comté)

    Informations professionnelles :
    Activité : Statistique

    Informations forums :
    Inscription : Octobre 2014
    Messages : 33
    Points : 48
    Points
    48
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    [*]Ton appel de fonction est incorrect: Ta fonction est censée modifier le pointeur self, mais ne reçoit en paramètre qu'une copie de celle-ci. Pour corriger ça, soit tu retourne la variable:
    Je viens de comprendre que calloc modifie la valeur du pointeur.

    Dans ma tête calloc «affectait de la mémoire au pointeur mais sans le modifier». Ce qui n’a probablement aucun sens. Les choses sont plus claires maintenant.

    Merci à tous les répondants. :-)

  8. #8
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 678
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 678
    Points : 30 965
    Points
    30 965
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Obofix le gaulois Voir le message
    Je viens de comprendre que calloc modifie la valeur du pointeur.
    Là je ne suis pas certain de bien comprendre. calloc ne peut en aucun cas modifier la valeur du pointeur (duquel d'ailleurs puisque cette fonction ne reçoit pas de pointeur en paramètre !!!!!!!)
    calloc alloue une zone mémoire et remplit cette zone de zéros. Ce n'est pas exactement la même chose...

    Citation Envoyé par Obofix le gaulois Voir le message
    Dans ma tête calloc «affectait de la mémoire au pointeur mais sans le modifier». Ce qui n’a probablement aucun sens.
    Idem. calloc n'affecte rien à personne. C'est comme si, dans le code suivant...
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    int carre(int n) {
        return n*n;
    }
     
    int main()
    {
        int x=carre(5);
    }
    ...tu disais "carre affecte une valeur à x" ; ce qui est totalement faux.

    calloc alloue de la mémoire, la remplit de zéros et renvoie l'adresse de la zone allouée. Charge à celui qui l'utilise (donc toi) de récupérer cette adresse pour la stocker dans un pointeur. C'est donc toi qui affecte la mémoire au pointeur, pas calloc...

    Citation Envoyé par Obofix le gaulois Voir le message
    Les choses sont plus claires maintenant.
    Sûr ???
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  9. #9
    Membre du Club
    Homme Profil pro
    Statistique
    Inscrit en
    Octobre 2014
    Messages
    33
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Jura (Franche Comté)

    Informations professionnelles :
    Activité : Statistique

    Informations forums :
    Inscription : Octobre 2014
    Messages : 33
    Points : 48
    Points
    48
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Là je ne suis pas certain de bien comprendre. calloc ne peut en aucun cas modifier la valeur du pointeur (duquel d'ailleurs puisque cette fonction ne reçoit pas de pointeur en paramètre !!!!!!!)
    calloc alloue une zone mémoire et remplit cette zone de zéros. Ce n'est pas exactement la même chose...
    Ce que je veux dire c'est que le pointeur à gauche du symbole égal est modifié si je comprends bien.
    Par exemple le code:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    int main()
    {
    int *tab; 
    printf("L'adresse de tab est %p.\n", tab); 
    tab =  calloc(5 , sizeof(int));
    printf("Maintenant, l'adresse de tab est %p.\n", tab);
    tab =  calloc(10 , sizeof(int));
    printf("Maintenant, l'adresse de tab est %p.\n", tab);
    }
    Me renvoie l'output

    L'adresse de tab est (nil).
    Maintenant, l'adresse de tab est 0xfc8420.
    Maintenant, l'adresse de tab est 0xfc8440.
    Ici on peut dire que la valeur du pointeur "tab" à été modifié lors dès différentes instructions qui font appel à calloc non? C'est ça que je voulais dire.

  10. #10
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    Par défaut
    Oui mais c'est bien l'affectation du résultat de calloc au pointeur et uniquement elle qui modifie sa valeur. L'appel à calloc est déjà clôturé lorsque cette opération a lieu.

    C'est pourquoi le programme suivant ne produit pas le même comportement que celui que tu as proposé ci-avant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    int main() {
        int *tab = NULL; 
        printf("L'adresse de tab est %p.\n", tab); 
        calloc(5 , sizeof(int));
        printf("Maintenant, l'adresse de tab est %p.\n", tab);
        calloc(10 , sizeof(int));
        printf("Maintenant, l'adresse de tab est %p.\n", tab);
    }
    Sve@r corrige tes imprécisions car elles mènent à l'ambiguïté ; c'est salutaire. Et ce n'est pas de la psychorigidité : un humain peut identifier l'ambiguïté et y manœuvrer, pas une machine. Elle ne se pose aucune question et fait exactement ce qu'on lui ordonne. Il est primordial de comprendre l'enchaînement des opérations.

  11. #11
    Membre du Club
    Homme Profil pro
    Statistique
    Inscrit en
    Octobre 2014
    Messages
    33
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Jura (Franche Comté)

    Informations professionnelles :
    Activité : Statistique

    Informations forums :
    Inscription : Octobre 2014
    Messages : 33
    Points : 48
    Points
    48
    Par défaut
    Citation Envoyé par Matt_Houston Voir le message
    Sve@r corrige tes imprécisions car elles mènent à l'ambiguïté ; c'est salutaire. Et ce n'est pas de la psychorigidité : un humain peut identifier l'ambiguïté et y manœuvrer, pas une machine. Elle ne se pose aucune question et fait exactement ce qu'on lui ordonne. Il est primordial de comprendre l'enchaînement des opérations.
    Pas de soucis. Je ne pense pas que Sve@r soit psychorigide. Je pense que je suis le premier bénéficiaire de l’effort de rigueur ici.

    Donc cette fois j’ai compris, (pense-je), que lorsque moi programmateur j’affecte le résultat de calloc à un pointeur, alors je modifie la valeur de ce pointeur.

  12. #12
    Membre expérimenté
    Avatar de sambia39
    Homme Profil pro
    No Comment
    Inscrit en
    Mai 2010
    Messages
    543
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loiret (Centre)

    Informations professionnelles :
    Activité : No Comment
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Mai 2010
    Messages : 543
    Points : 1 745
    Points
    1 745
    Par défaut
    Bonjour
    Attention, allocation d'une mémoire dynamique est à la charge du programmeur.
    La valeur de votre pointeur n'a pas été modifiée, mais écraser (perdu) ce qui veut dire que lors du deuxième appel de la fonction d'allocation dynamique vous avez remplacé l'adresse de la mémoire allouée précédemment par une nouvelle adresse mémoire acquise grâce à un nouvel appel de la fonction dynamique calloc(...)(ce qui explique les différente adresse obtenu) et par la même occasion vous avez créé une fuite mémoire. Je m'explique, lorsque la mémoire est allouée dynamiquement une variable pointeur contiendra l'adresse mémoire de celle-ci (pour faire simple elle pointe vers la mémoire allouée) et si un nouvelle appel de la fonction d'allocation mémoire est effectué (et que tout se passe sans encombre), une nouvelle adresse mémoire vous est attribuer (une nouvelle zone mémoire) et donc si vous utilisez le même pointeur que la précédente adresse mémoire le lien avec votre précédente zone mémoire est rompue et donc la mémoire précédemment allouée est perdu et vous obtenez une fuite mémoire, car le pointeur pointe sur une autre adresse mémoire. Pour éviter la fuite mémoire, il est impératif de sauvegarder l'ancienne adresse mémoire dans une autre variable pointeur et tous comme une allocation, il faut restituer l'ensemble des mémoires alloués au système (free).

    Citation Envoyé par Obofix le gaulois Voir le message
    Pas de soucis. Je ne pense pas que Sve@r soit psychorigide. Je pense que je suis le premier bénéficiaire de l’effort de rigueur ici.

    Donc cette fois j’ai compris, (pense-je), que lorsque moi programmateur j’affecte le résultat de calloc à un pointeur, alors je modifie la valeur de ce pointeur.
    @Sve@r a raison, car la fonction calloc / malloc ne modifier aucun pointeur, mais retourne des adresses mémoire (zone mémoire allouée) cas contraire NULL en aucune façon, il modifie un pointeur. Le fait d'utiliser une variable pointeur permettra juste de contenir l'adresse de la zone mémoire alloué et donc si l'on se contente d'utiliser la même variable avec des appels de la fonction calloc / malloc, on ne fait qu'écraser l'adresse mémoire contenue dans la variable pointeur et en même temps on crée une fuite/ des fuites mémoire. C'est donc la façon dont t'on utilise la variable pointeur qui est source d'ennuis, mais la fonction d'allocation dynamique calloc / malloc en elle-même ne modifie aucun pointeur ce n'est pas sa fonction. L'erreur vient donc du programmeur. Et dans votre l'exemple, c'est le cas. L'emploi de la même variable pointeur, mais avec des tailles mémoires différentes donc obtention d'adresse mémoire différente et fuite mémoire par la même occasion.
    À bientôt
    Celui qui peut, agit. Celui qui ne peut pas, enseigne.
    Il y a deux sortes de savants: les spécialistes, qui connaissent tout sur rien,
    et les philosophes, qui ne connaissent rien sur tout.
    George Bernard Shaw

  13. #13
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    Par défaut
    Puisque l'on y est, autant évoquer maintenant le cas particulier de realloc qui est souvent très mal employée par les débutants.

    Si cette fonction ne modifie pas à proprement parler la valeur du pointeur qu'on lui passe, en cas de succès (allocation réussie) c'est tout comme puisqu'elle l'invalide et on ne peut rien faire avec un pointeur non valide, pas même consulter sa valeur !

    Je n'avais pas souligné ceci par souci de confusion mais il est temps je pense de le préciser :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    int main() {
        int *p; // pointeur invalide tant qu'il n'est pas initialisé
        if (p) // /!\ ERREUR! pointeur invalide : comportement indéterminé
            printf("ptr : %p\n", (void *)p); // /!\ ERREUR! comportement indéterminé pour les mêmes raisons
     
        p = calloc(1, sizeof *p);
        printf("ptr : %p\n", (void *)p); // OK, pointeur valide (contient NULL ou adresse acquise)
        free(p); // OK, pointeur toujours valide
     
        p = malloc(sizeof *p); // réinitialisation du pointeur pour la suite
     
        int *q = realloc(p, sizeof *p); // note : passer NULL à realloc est autorisé (équivaut à un appel à malloc)
        printf("temp ptr : %p\n", (void *)q); // OK, pointeur valide (contient NULL ou adresse acquise)
        if (q) { // OK, pointeur toujours valide
            // realloc a réussi
            printf("ptr : %p\n", (void *)p); // /!\ ERREUR! pointeur invalidé par realloc : comportement indéterminé
            p = q;
            printf("ptr : %p\n", (void *)p); // OK, pointeur réinitialisé et donc valide
        }
        else
            // realloc a échoué
            printf("ptr : %p\n", (void *)p); // OK, pointeur valide (avec la valeur qu'il avait avant l'appel à realloc)
     
        free(p); // OK, note : passer NULL à free est autorisé (no-op)
     
        printf("ptr : %p\n", (void *)p); // /!\ ERREUR! pointeur invalidé par free
        printf("temp ptr : %p\n", (void *)q); // /!\ ERREUR! pointeur invalidé par free, dans le cas où realloc avait réussi
     
        return 0;
    }

  14. #14
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 678
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 678
    Points : 30 965
    Points
    30 965
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Matt_Houston Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
        free(p); // OK, pointeur toujours valide
     
        int *q = realloc(p, sizeof *p); // note : passer NULL à realloc est autorisé (équivaut à un appel à malloc)
    Euh tu as libéré p puis tu lui fais une demande de realloc. Je n'ai pas le détail de la norme concernant ce point mais je suis intuitivement presque sûr que ça donne un comportement indéterminé.
    Perso j'aurais rajouté p=NULL entre les deux pour retrouver une adresse correcte. Et accessoirement dans ce cas, moi j'écris alors free(p), p=NULL; pour bien montrer que les deux instructions sont étroitement liées...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  15. #15
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    Par défaut
    C'est évidemment un UB (pointeur invalidé par free). Taper un exemple à la volée sans vraiment se relire : boum ! Coquille corrigée, merci Sve@r !

    Je n'entendais pas passer explicitement NULL ici à realloc car je cherchais à montrer une utilisation typique de la fonction.

  16. #16
    Membre expérimenté
    Avatar de sambia39
    Homme Profil pro
    No Comment
    Inscrit en
    Mai 2010
    Messages
    543
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loiret (Centre)

    Informations professionnelles :
    Activité : No Comment
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Mai 2010
    Messages : 543
    Points : 1 745
    Points
    1 745
    Par défaut
    Bonsoir,
    Attention, tout de même, a la ligne 8, la fonction free libère la mémoire pointée par le pointeur, mais ne modifie en aucune façon la valeur du pointeur (commentaire erroné) ce n’est que la mémoire qui a été libérée; et donc techniquement vous ne passer pas un pointeur NULL (d’où la pertinence de @Sve@r de toujours mettre un NULL après l’appel de la fonction free), mais un pointeur contenant une adresse invalide. Et lors de l’appel de la fonction realloc(...), on lui passe un pointeur avec cette adresse invalide. Et c’est tout à fait normal que la fonction realloc(...)échouer (et c'est le cas dans votre exemple) parce qu’il ne peut en aucune façon manipuler une mémoire non allouée ; préalablement libérée ou qu’elle possède une adresse invalide donc il s’arrête net avec un segment défaut pour les raisons suivantes :
    • Il est bon à savoir que *p n’a strictement aucun sens avant tout type d’allocation dynamique ou après l’appel de la fonction free. Le passer comme paramètre de fonctions pour la taille renverra juste la taille, tu types que pointe le pointeur "p" ce qui alloue de la mémoire dans le cas du premier malloc ou calloc, mais en revanche dans le cas de realloc, le pointeur "p" ne peut pas être manipulé(la zone mémoire que pointe "p" a été libérer); car elle contient une adresse invalide (info: realloc libère le pointeur "p" quand elle réussit) et donc elle échoue avec une erreur de segmentation; plus précisément à cause d’un double free de "p".
    • En déclarant une variable pointeur int *p celle-ci contient une adresse invalide même-si elle n’est pas initialisée, l’utiliser avec instruction if signifie "si la valeur de p est différente de NULL" alors afficher la valeur cas contraire l’instruction est ignorée et dans l'exemple en a pas un comportement indéterminé, mais bien ce qui est écrit que le programme réalise, car la valeur de "p" ne vaut pas NULL
      Code C : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
       
      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
       
      int main( void ){
       
          int *p;
          extern int errno;
       
          errno = 0;
          if( p ){
              (void)fprintf(stderr, "ADDR PTR\t:%p\n", (void*)p );
              p = realloc( p, sizeof *p );
              if( NULL == p ){
                  (void)fprintf(stderr, "Erreur(%d)\t:%s\n\t:%s\n", errno,
                      "Erreur re-allopcation pointeur", strerror(errno) );
                  return EXIT_FAILURE;
              }
       
              //instruction jamais atteinte
              free( p );
              p = NULL;
          }
       
          //si null mais instruction jamais atteinte
          (void)fprintf(stderr, "ADDR PTR NULL\t:%p\n", (void*)p );
          p = realloc( p, sizeof *p );
              if( NULL == p ){
                  (void)fprintf(stderr, "Erreur(%d)\t:%s\n\t:%s\n", errno,
                      "Erreur re-allopcation pointeur", strerror(errno) );
                  return EXIT_FAILURE;
              }
       
          //instruction jamais atteinte
          free( p );
          p = NULL;
       
          return EXIT_SUCCESS;
      }
    Celui qui peut, agit. Celui qui ne peut pas, enseigne.
    Il y a deux sortes de savants: les spécialistes, qui connaissent tout sur rien,
    et les philosophes, qui ne connaissent rien sur tout.
    George Bernard Shaw

  17. #17
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Là où ça devient bizarre, c'est qu'en théorie le seul fait de lire un pointeur "sauvage" (même sans le déréférencer) est un comportement indéterminé: La ligne (void)fprintf(stderr, "ADDR PTR\t:%p\n", (void*)p ); pourrait invoquer des démons nasaux (surtout de nos jours où un compilo qui voit un comportement indéterminé peut décider d'optimiser agressivement).
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  18. #18
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    Par défaut
    Citation Envoyé par sambia39 Voir le message
    Attention, tout de même, a la ligne 8, la fonction free libère la mémoire pointée par le pointeur, mais ne modifie en aucune façon la valeur du pointeur
    Ben « si » justement, c'est toute la raison d'être de l'exemple :

    Citation Envoyé par Matt_Houston Voir le message
    Si cette fonction ne modifie pas à proprement parler la valeur du pointeur qu'on lui passe, en cas de succès (allocation réussie) c'est tout comme puisqu'elle l'invalide et on ne peut rien faire avec un pointeur non valide, pas même consulter sa valeur !
    On ne peut utiliser un pointeur invalide en aucun cas, la notion de sa valeur n'a plus de sens. Le compilateur est autorisé à en faire n'importe quoi.


    Citation Envoyé par sambia39 Voir le message
    (commentaire erroné)
    Lequel ? Pourquoi ? Je précise si ce n'était clair que chaque commentaire fait référence à la validité du pointeur au moment de son utilisation à la ligne considérée.


    Citation Envoyé par sambia39 Voir le message
    [...] adresse invalide [...]
    Tout comme la notion de valeur pour un pointeur invalide, ce terme est dénué de sens. C'est le pointeur lui-même qu'on qualifie de valide ou d'invalide.


    Citation Envoyé par sambia39 Voir le message
    • Il est bon à savoir que *p n’a strictement aucun sens avant tout type d’allocation dynamique ou après l’appel de la fonction free.
    *p je ne sais pas, mais sizeof *p (qui est évalué à la compilation - sauf si p est un VLA) possède un sens certain et précis.

    Citation Envoyé par sambia39 Voir le message
    Le passer comme paramètre de fonctions pour la taille renverra juste la taille, tu types que pointe le pointeur "p" ce qui alloue de la mémoire dans le cas du premier malloc ou calloc, mais en revanche dans le cas de realloc, le pointeur "p" ne peut pas être manipulé(la zone mémoire que pointe "p" a été libérer); car elle contient une adresse invalide (info: realloc libère le pointeur "p" quand elle réussit) et donc elle échoue avec une erreur de segmentation; plus précisément à cause d’un double free de "p".
    Il est important de faire attention à la terminologie particulièrement lorque l'on se targue de pallier une imprécision : sizeof n'est pas une fonction mais un opérateur.

    Je n'ai absolument rien entravé à ce que tu racontes à propos de realloc.


    Citation Envoyé par sambia39 Voir le message
    • En déclarant une variable pointeur int *p celle-ci contient une adresse invalide même-si elle n’est pas initialisée
    Non. Ce n'est pas ce que dit la norme.

    Citation Envoyé par sambia39 Voir le message
    l’utiliser avec instruction if signifie "si la valeur de p est différente de NULL" alors afficher la valeur cas contraire l’instruction est ignorée et dans l'exemple en a pas un comportement indéterminé, mais bien ce qui est écrit que le programme réalise,
    Si. C'est bien un UB pur et simple.

    Citation Envoyé par sambia39 Voir le message
    car la valeur de "p" ne vaut pas NULL
    Pas plus NULL qu'autre chose étant donné qu'évoquer sa valeur est absurde.


    Il faut à tout prix éviter d'extrapoler le comportement d'un programme particulier dans un environnement particulier généré par un compilateur particulier à partir de et pour des architectures particulières.

    Seule la norme fait loi et elle dit que l'utilisation d'un pointeur invalide produit un comportement indéterminé, qu'il soit invalide faute d'initialisation, à la suite d'un free (dangling) ou d'une opération arithmétique erronée.

  19. #19
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 678
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 678
    Points : 30 965
    Points
    30 965
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Matt_Houston Voir le message
    Ben « si » justement, c'est toute la raison d'être de l'exemple :
    Euh non je ne crois pas. Ou alors il y a une incompréhension dans les termes.
    Pour moi, la fonction free(p) libère la mémoire qui avait été allouée à "p" mais ne modifie pas la valeur contenue dans p (comme d'ailleurs c'est le cas pour toute fonction qui reçoit une simple variable et non son adresse => elle ne reçoit que la copie de sa valeur et n'a alors aucune possibilité de modifier ladite variable).

    Exemple: si on alloue 123 octes de mémoire et que l'on stocke l'adresse de la zone allouée dans p dans le style char *p=malloc(123). Imaginons que la mémoire ait été allouée à l'adresse 0x1000. On aura alors p=0x1000.
    Si ensuite on libère p => free(p), alors l'adresse 0x1000 ne correspond plus à une zone valide mais la variable "p" contient toujours cette valeur 0x1000... non ?
    Le soucis, c'est que comme le dit medinoc (et Emmanuel Delahaye m'avait dit la même chose quand il était encore là), c'est que tout comme un état quantique, on n'a plus le droit d'aller regarder sa valeur. Enfin quand je le fais quand-même ben "p" ne change pas après le free()...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  20. #20
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 368
    Points : 23 620
    Points
    23 620
    Par défaut
    Hello,

    Citation Envoyé par Matt_Houston Voir le message
    Il faut à tout prix éviter d'extrapoler le comportement d'un programme particulier dans un environnement particulier généré par un compilateur particulier à partir de et pour des architectures particulières. Seule la norme fait loi et elle dit que l'utilisation d'un pointeur invalide produit un comportement indéterminé, qu'il soit invalide faute d'initialisation, à la suite d'un free (dangling) ou d'une opération arithmétique erronée.
    C'est vrai en soi (et j'avoue que j'ai la flemme d'aller voir maintenant la façon dont la norme est rédigée sur ce point précis. Je le ferai demain à tête reposée), mais cela m'évoque quand même deux choses : pour moi, « valeurs indéfinies » ou « états indéfinis » n'impliquent pas forcément « comportements indéfinis » (UB). Ce sont deux choses différentes. Je veux bien m'incliner malgré tout si c'est écrit tel quel dans la norme.

    En particulier : une valeur peut être formellement valide, en elle-même, même si elle ne correspond à rien. C'est pratique par exemple pour indiquer l'adresse que l'on vient de libérer par exemple. Mais plus généralement, pour dire si un pointeur référence ou non quelque chose de valide, il faut d'abord savoir ce que l'on entend par valide : lorsqu'on programme en standalone, ce sont free() et malloc() eux-mêmes qui n'ont plus de définition. Or, les concepts intrinsèques du langage C transcendent sa bibliothèque standard : un pointeur reste le même objet dans tous les cas, même en l'absence de mécanisme de libération.

    Maintenant, sur le plan ontologique, je suis tout-à-fait ton raisonnement : si on considère que l'adresse en binaire (formellement un nombre entier, dans les faits) n'est que l'implémentation technique du concept de référence introduit par un pointeur, on peut effectivement penser que la notion même de référence à un objet inexistant n'a pas de sens, cette référence étant par nature inexistante. On propage donc sémantiquement l'invalidation d'une entité à la manière d'un "ON DELETE CASCADE" au sein d'un jeu de contraintes d'intégrité. D'ailleurs, en dehors de ces considérations philosophiques, j'admets tout-à-fait que c'est une méthodologie propre pour mettre en place ces mêmes contraintes au cœur d'une phase de développement.

    Mais dans ce cas, a-t-on le droit de faire référence à quelque chose qui A EXISTÉ de façon tout-à-fait valide ? Et par là, j'entends seulement l'évoquer, par l'évaluer. Ça reste légitime pour savoir si plusieurs entités parlent bien toutes d'une même chose, eût-elle cessé d'exister.

    De façon plus terre à terre, peut-on raisonnablement estimer qu'un programme en cours d'exécution peut conserver ad vitam de façon sûre des pointeurs réputés invalides à la seule condition qu'on ne les évalue pas (moment auquel il ne demanderait qu'à nous exploser à la figure) ? Ce principe même n'invaliderait-il pas de fait le programme entier ?


    Si on ramène le même problème à un entier int, et que l'on déclare une variable x avec int x sans l'initialiser dans la foulée, est-il légitime de dire que l'on peut garantir que la valeur de x sera forcément un entier (par définition) et donc évaluable de façon sûre même si elle est indéfinie ?

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Erreur sur une requête en utilisant la fonction somme
    Par TheFantasyRide dans le forum Requêtes et SQL.
    Réponses: 3
    Dernier message: 21/04/2009, 18h55
  2. Réponses: 10
    Dernier message: 07/08/2008, 09h09
  3. Réponses: 3
    Dernier message: 27/05/2008, 23h07
  4. [PHPMailer] Erreur lors de l'utilisation de la fonction mail
    Par onlytime dans le forum Bibliothèques et frameworks
    Réponses: 1
    Dernier message: 04/04/2008, 16h11
  5. Réponses: 2
    Dernier message: 20/04/2007, 11h52

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo