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. #21
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 684
    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 684
    Points : 30 973
    Points
    30 973
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    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 ?
    Non et oui.
    Non on ne peut pas dire que x est un entier. x n'est qu'un conglomérat de bits associés en octets et concaténés.
    Mais c'est la façon dont le système lit ces bits (bit de signe, puissances de 2, etc) qui en fait un entier. La valeur de ces bits a beau être indéfinie, elle existe cependant. Elle a beau être indéfinie, on peut quand-même dire avec certitude que certains sont à 0 et certains sont à 1 et que cette suite de 0 et de 1 correspondra fatalement à une équivalence avec un des infinis nombres entiers existants...

    La question pourrait être plus difficile avec les float. Parce que le codage d'un float (avec sa mantisse et son exposant) est plus complexe que celui d'un entier (qui n'a en fait qu'un bit de signe et n bits de puissance de 2). Et on pourrait hypothéser que peut-être une suite particulière de 0 et de 1 dans un x déclaré comme float n'a pas de signification (toutefois j'en doute quand-même un peu).
    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]

  2. #22
    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 622
    Points
    23 622
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Non et oui.
    Non on ne peut pas dire que x est un entier. x n'est qu'un conglomérat de bits associés en octets et concaténés.
    Ce n'est pas ce que j'ai dit (je l'évoque au dessus en parlant des pointeurs et soulignant que sur un point de vue purement formel, il ne s'agit que des détails de l'implémentation).

    Mais c'est la façon dont le système lit ces bits (bit de signe, puissances de 2, etc) qui en fait un entier. La valeur de ces bits a beau être indéfinie, elle existe cependant. Elle a beau être indéfinie, on peut quand-même dire avec certitude que certains sont à 0 et certains sont à 1 et que cette suite de 0 et de 1 correspondra fatalement à une équivalence avec un des infinis nombres entiers existants...
    Ça c'est ce que je dis au-dessus, par contre. Mais même dans ce cas, ce que je voulais dire, c'est que là encore sur un plan purement formel, en se cantonnant à la norme et en faisant abstraction de l'implémentation technique, écrire int x garantit que x est forcément un entier et que sa valeur sera forcément comprise entre les limites de tolérance spécifiées par la norme. Donc, même indéfini, l'évaluer doit être forcément sûr, même en dehors des spécificités techniques d'une machine donnée.

  3. #23
    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
    Citation Envoyé par Obsidian Voir le message
    Mais même dans ce cas, ce que je voulais dire, c'est que là encore sur un plan purement formel, en se cantonnant à la norme et en faisant abstraction de l'implémentation technique, écrire int x garantit que x est forcément un entier et que sa valeur sera forcément comprise entre les limites de tolérance spécifiées par la norme. Donc, même indéfini, l'évaluer doit être forcément sûr, même en dehors des spécificités techniques d'une machine donnée.
    Ce qui est hélas faux en théorie: La norme garantissant -32767 à +32767, tu pourrais avoir une architecture respectant le standard où -32768 est l'équivalent d'un signaling NaN.
    Tout comme il y a, parait-il, des architectures où le seule fait de charger une valeur "invalide" (ce qui peut vouloir dire "pas dans la table des pages valides du processus") dans un registre d'adresse cause une exception (même si on n'essaie pas de déréférencer cette adresse).
    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.

  4. #24
    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,
    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.
    Dans mon premier message, j'attire l'attention sur la fuite mémoire, mais aussi la façon dont la variable de type pointeur est employé. Et que les fonctions d'allocations malloc(..)et calloc(...) en elle-même ne modifient pas un pointeur, mais quel, renvoient juste des adresses que l'on doit stocker dans une ou des variables pointeurs.

    Dans le deuxième message, j'attire l'attention sur les commentaires qui porte à confusion la compréhension du code source, mais aussi sur l'importance de ne pas employer une variable de type pointeur contenant une adresse mémoire invalide avec les fonction realloc(...) et free.


    Citation Envoyé par Matt_Houston Voir le message
    (1)Non. Ce n'est pas ce que dit la norme.
    ......
    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.
    1Voilà comment moi, je vois les choses: Quand on déclare une variable pointeurint*p; sans initialisation. On déclare juste une variable pointeur qui pointe sur un objet du type "type" (possédant une valeur aléatoire sauf, si global ou static) .Donc lorsque l'on déclare une variable pointeur "p" sur un objet de type "type", il ne pointe pas vraiment sur l'objet de ce type "type", mais plutôt vers un endroit quelconque de la mémoire auquel il n'est pas conseillé d’y accéder (donc une adresse "invalide" puisqu’il pointe quelque part et sur quoi....) et si vous tentez d’accéder à cet endroit de la mémoire il y a systématiquement un crash voir pire.

    Donc, pour mon cas à moi (pour faire simple) un pointeur déclaré sans initialisation est un pointeur qui contient une adresse invalide et tenter d’y accéder à cette adresse n’est pas chose légale ou conseillée. Il est donc primordial de l’initialiser (donc le mettre) à NULL (grâce au pointeur invalide de référence "NULL"). Et à ce sujet, la norme n’impose en rien quand a ça valeur, adresse ou représentation. Cependant de façon générale, c’est l’adresse zéro qui est utilisée (en ma connaissance, je n’ai à ce jour, vu aucune dérogation à cette règle).Et NULL est très utile pour déceler les erreurs d’adressage on l'affectant à la variable pointeur de façon temporaire dans l'attente de donner une vraie valeur (adresse valide); et l'avantage est qu'en cas d'oublie d'initialisation réel du pointeur (adresse valide) il ne se produira pas un crash mal propre (sous réserve de gérer les erreurs avants et prendre les mesure adapté)
    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
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    int main( void ){
     
        int *p = NULL;
        free( p );
        int *q = realloc( p, sizeof *p );
        if( NULL == p )
            (void)puts("Error");
        free( q );
        q = NULL;
        return EXIT_SUCCESS;
    }
    Tant dit qu'avec un pointeur contenant une adresse invalide le crash est immédiat voir pire "comportement indéterminé". (dans un contexte dite de "mémoire virtuel",elle est également considéré comme étant une adresse erronée ou invalide employer également pour déceler les erreurs d'adressage); " Unspecified behavior: The value of a pointer to an object whose lifetime has ended is used (6.2.4)."
    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
     
    int main( void ){
     
        int *p;
        free( p );     // cause: malloc: *** error for object 0x7fff5fbff630: pointer being freed was not allocated
        /* instruction ci-dessous
         * jamais atteinte
         */
        int *q = realloc( p, sizeof *p );
        if( NULL == p )
            (void)puts("Error");
        free( q );
        q = NULL;
        return EXIT_SUCCESS;
    }

    Et donc pour moi une variable pointeur non initialisé est une variable pointeur qui contient une adresse invalide.
    Citation Envoyé par sambia39
    En déclarant une variable pointeur int *p celle-ci contient une adresse invalide même-si elle n’est pas initialisée
    Citation Envoyé par Matt_Houston Voir le message
    Non. Ce n'est pas ce que dit la norme.
    Citation Envoyé par Matt_Houston Voir le message
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int main() {
        int *p; // pointeur invalide tant qu'il n'est pas initialisé
       .......
    Que dit la norme alors a ce sujet , si l'on n'est pas d'accord sur le principe qu'un pointeur invalide contient une adresse mémoire invalide et qu'il n'est pas conseiller d'y accéder ?(pour ma part l'adresse invalide contenue dans la variable pointeur définis si celui-ci est valide où invalide et non, le pointeur en lui-même qui se qualifier de valide ou invalide. D'un autre côté, c'est également à la charge du programmeur de faire attention à l'emploi de ces pointeurs et de qualifier celui-ci selon le contexte, le moment et l'adresse mémoire que le pointeur contient de valide ou invalide).

    Citation Envoyé par Matt_Houston Voir le message
    2On 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.
    ...........
    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.
    Citation Envoyé par sambia39 Voir le message
    Bonsoir,

    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.
    2Initialisés avec NULL des pointeurs c'est aussi dire que la variable pointeur pointe sur rien ou variable "pointeur en attente d'adresse valide" évitant ainsi tout amalgame . Il est alors intéressant de se poser la question de savoir ce que signifie *p avant toute allocation dynamique ou après avoir libéré la mémoire et si elle a un sens et surtout que dit la norme à ce sujet je suis bien curieux de le savoir. Et si pour vous sizeof *p a un sens ce qui est normale avec c’est tout simplement parce que l’opérateur sizeof renvoie juste la taille du type de la variable pointeur mais dans un autre context que veux t'il dire sans le sizeof (et merci pour la correction "opérateur" et non fonction). Au passage, on est dans un contexte d’exécution dynamique donc tout se fait au moment de l’exécution ; pas pendant la compilation (à la limite les warnings pour informer de la non-initialisation où que l’on considérer tout warning comme étant des erreurs lors de la compilation ou peut-être que le compilateur effectue des optimisation agressive mettant p à NULL).

    Citation Envoyé par Matt_Houston Voir le message
    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.
    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.
    Ok, tout d'abord, le but était de faire attention sur la ré-allocation avec une variable pointeur contenant une adresse invalide et qu’a cette ligne précisément le programme est avorté à cause d’un double free. Le commentaire est erroné parce qu’à la ligne indiquée, on sollicité une fonction qui libérer la mémoire allouée , mais l’adresse contenue dans le pointeur est inchangée. A ce moment-là on ne peut plus qualifié la variable pointeur elle-même de valide où invalide car on ne sais rien a l'issue de la fonction si le pointeur est encore valide ou non d'ou la nécessité et l'importance de mettre a NULL après l'appel de free pour invalidé le pointeur. Donc, a la ligne indiquée le commentaire est inappropriée, il est plus utile et simple de mettre "/*libération mémoire après utilisation du pointeur p*/.
    Rappel que La fonction free échoué si on essaie de libérer de la mémoire qui n'a pas été allouée.
    • La fonction free ne change pas le contenu du pointeur; il est conseillé d'affecter la valeur zéro (NULL de préférence) au pointeur immédiatement après avoir libéré le bloc de mémoire qui y était attaché.
    • Si nous ne libérons pas explicitement la mémoire à l'aide free, alors elle est libérée automatiquement à la fin du programme.


    Citation Envoyé par Sve@r Voir le message
    Citation Envoyé par Matt_Houston Voir le message
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
     
    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...

    Citation Envoyé par Matt_Houston Voir le message
    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.
    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.
    Je pense que vous n'avez pas compris où je me suis très mal exprimé sur le sujet, je parle de ce que Sve@r mis en évidence comme code source, j'ai donc souligné le crash que pouvais occasionner un pointeur invalide avec la fonction d'allocation realloc.

    à 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

+ Répondre à la discussion
Cette discussion est résolue.
Page 2 sur 2 PremièrePremière 12

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