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 :

Fonctionnement malloc() et free()


Sujet :

C

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2018
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 27
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2018
    Messages : 6
    Points : 6
    Points
    6
    Par défaut Fonctionnement malloc() et free()
    Bonjour à tous,

    je débute en programmation et j'ai énormément de mal à comprendre le fonctionnement de malloc() et de free().

    Tel que je l'ai compris, malloc() permet d'allouer de la mémoire de façon dynamique au programme, free() permet de la libérer, donc de permettre au système de réutiliser cette mémoire comme il l'entend. Cependant, en pratique j'ai l'impression que ca ne fonctionne pas comme ça.

    On me demande de recoder plusieurs fonction de la libcet pour mes tests unitaires, je joue avec des fonctions qui utilisent malloc() et free() mais j'ai l'impression que malloc(SIZE) m'alloue beaucoup plus que SIZE. En cherchant un peu j'ai cru comprendre quemalloc, lors de son premier appel, va allouer un minimum d'une 'PAGESIZE' soit environ 128ko d'après le man mais j'ai essayé 1 000 000 et ca passe mais 1 000 000 000 ca passe. Premier problème car lorsque je code des programme qui utilise bien moins que 10 000 octets j'arrive a segfault alors que j'alloue bien toutes mes variables. (Exemple Splitwhitespace)

    Mon autre interrogation porte sur free(). En cherchant un peu, j'ai compris que "libérer" la mémoire signifie permettre au système d'utiliser la mémoire allouer précèdemment avec malloc(), realloc(), calloc(). Mais donc, selon moi, si la mémoire est libérée alors le programme ne peut plus la réutiliser à moins de redemander l'autorisation au système.

    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
    #include "libft.h"
    #include <stdio.h>
    #include <stdlib.h>
     
    int main()
    {
     
       int i = 0;
     
       char *s_test = (char*)malloc(sizeof(char) * (7));
     
       s_test[0] = 'c';
       s_test[1] = 'o';
       s_test[2] = 'u';
       s_test[3] = 'c';
       s_test[4] = 'o';
       s_test[5] = 'u';
       s_test[6] = 0;
       printf("%s\n", s_test);
       while (i < 7)
          printf("%p\n", &s_test[i++]);
       free(s_test);
       i = 0;
       s_test[0] = 'c';
       s_test[1] = 'o';
       s_test[2] = 'u';
       s_test[3] = 'c';
       s_test[4] = 'o';
       s_test[5] = 'u';
       s_test[6] = 0;
       printf("%s\n", s_test);
       while (i < 7)
          printf("%p\n", &s_test[i++]);
       return (0);
    }
    et le retour de ce programme :

    coucou

    0x7fe96cc02600

    0x7fe96cc02601

    0x7fe96cc02602

    0x7fe96cc02603

    0x7fe96cc02604

    0x7fe96cc02605

    0x7fe96cc02606

    coucou

    0x7fe96cc02600

    0x7fe96cc02601

    0x7fe96cc02602

    0x7fe96cc02603

    0x7fe96cc02604

    0x7fe96cc02605

    0x7fe96cc02606

    Si j'ai cherché à afficher les adresses, c'était pour voir si free() agissait sur les adresses, et j'écris caractères par caractères car j'avais l'impression que c'était différent de faire s_test = "coucou".

    Egalement, toujours sur free(), le programme suivant :

    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
    #include "libft.h"
     
    void   ft_strdel(char **as)
    {
        size_t index;
        char   *as_l;
     
        as_l = *as;
        index = 0;
        while (as_l[index] != 0)
        {
           free(&as_l[index]);
           index += 1;
        }
        *as = NULL;
    }


    avec le main associé :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include "libft.h"
    #include <stdio.h>
    #include <stdlib.h>
     
    int main()
    {
        char *s_test = (char*)malloc(sizeof(char) * (7));
     
        s_test = "coucou";
        ft_strdel(&s_test);
        return (0);
    }
    me renvoie ca :

    e1r2p7% ./a.out

    a.out(75304,0x7fffc69683c0) malloc: *** error for object 0x10bb88fa8: pointer being freed was not allocated

    *** set a breakpoint in malloc_error_break to debug

    zsh: abort ./a.out

    En cherchant un peu, cette erreur signifie que j'essaie de free quelque chose qui n'a pas été allouer dynamiquement donc avec malloc() et ses copines, mais la pourtant j'ai alloué s_test juste avant.

    Désolé si ces questions peuvent sembler basique mais je ne comprend vraiment pas l'allocation dynamique de la memoire en pratique et je sens que j'ai vraiment besoin de le comprendre maintenant pour avancer. Si certains ont des ressources qui explique en détails ces processus je suis preneur !

    Merci de m'avoir lu jusque là.
    Cordialement

  2. #2
    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
    Ce sera plus facile à comprendre si tu assimiles la notion de pages (qui, si elle est cachée complètement derrière malloc() sous Linux, est visible avec les fonctions à bas niveau de Windows).

    Pour dire ça simplement, le kernel n'alloue pas la mémoire au processus au bit près, mais page par page (une page faisant 4K sur un Windows 32 bits). Derrière, un autre mécanisme (situé toujours dans le Kernel sous Linux, mais dans la libc sous Windows) gère les différentes zones allouées par malloc() et retournées par free().

    La théorie, c'est qu'une page peut être "rendue" au système d'exploitation dès qu'il n'y a plus aucune zone mémoire retournée par malloc() qui en dépend (en gros, quand elles ont toutes été passées à free()). Mais rien n'empêche le système de faire autrement (toujours garder une page sous la main, ne rendre les pages que si une notification de mémoire basse est reçue, etc.)

    Là où ça devient problématique, c'est que tu ne peux pas prédire ce qui est alloué dans la même page ou non, et surtout tu ne peux pas prédire quand un free() va mener à une libération de page. Ce qui fait qu'écrire dans une zone mémoire libérée par free() est un comportement indéfini.

    PS: Pour ce qui est de Windows, le modèle en couches est comme tel: malloc(42) appelle HeapAlloc(GetProcessHeap(), 0, 42), qui appellera VirtualAlloc si le processus a besoin de pages mémoire supplémentaires.
    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.

  3. #3
    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 P.N.J Voir le message
    Egalement, toujours sur free(), le programme suivant :

    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 "libft.h"
     
    void   ft_strdel(char **as)
    {
        size_t index;
        char   *as_l;
     
        as_l = *as;
        index = 0;
        while (as_l[index] != 0)
        {
           free(&as_l[index]);
           index += 1;
        }
        *as = NULL;
    }



    avec le main associé :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include "libft.h"
    #include <stdio.h>
    #include <stdlib.h>
     
    int main()
    {
        char *s_test = (char*)malloc(sizeof(char) * (7));
     
        s_test = "coucou";
        ft_strdel(&s_test);
        return (0);
    }
    me renvoie ca :

    Code X : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    e1r2p7% ./a.out
    
    a.out(75304,0x7fffc69683c0) malloc: *** error for object 0x10bb88fa8: pointer being freed was not allocated
    
    *** set a breakpoint in malloc_error_break to debug
    
    zsh: abort      ./a.out
    En cherchant un peu, cette erreur signifie que j'essaie de free quelque chose qui n'a pas été allouer dynamiquement donc avec malloc() et ses copines, mais la pourtant j'ai alloué s_test juste avant.
    Regarde bien le code de ft_strdel(): Il fait des désallocations en boucle. Il ne demande donc pas juste un pointeur sur UN pointeur alloué, mais un pointeur sur le premier d'un tableau, se terminant par un pointeur nul. Ce qui signifie que l'appel ft_strdel(&s_test); est une erreur.
    Ceci par contre, devrait marcher:
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include "libft.h"
    #include <stdio.h>
    #include <stdlib.h>
     
    int main()
    {
        char *s_test = malloc(sizeof(char) * (7));
        char* s_tableau[] = { s_test, NULL };
     
        s_test = "coucou";
        ft_strdel(s_tableau);
        return (0);
    }
    PS: On ne caste pas le retour de malloc() en C, ça ne fait rien d'autre que cacher des erreurs potentielles.
    En C, le type void* est implicitement convertible en char*. Si ton compilo gueule, c'est que tu compiles en C++ au lieu de compiler en C.
    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. #4
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2018
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 27
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2018
    Messages : 6
    Points : 6
    Points
    6
    Par défaut
    Bonjour, merci de ta réponse mais il y a quelque chose que je ne comprend toujours pas avec ton main corriger.

    Tu définis un char ** qui contient mon char * et le pointeur NULL et tu le passes dans ft_strdel, concordance de type d'argument, ca passe j'ai aucun soucis la dessus.Mais je ne comprend pas pourquoi tu ajoute comme deuxième élément le pointeur NULL, quel est son rôle dans tout ca ?

    Egalement, quand j'affiche s_test après un appel de ft_strdel, il continue de m'afficher "coucou" alors que j'ai tenté de mettre son adresse sur NULL et donc si je le print il est censé m'afficher "(null)" ?

    Cordialement

    EDIT: Apres relecture je ne comprend pas la difference de mettre l'adresse de ma chaine de caractere dans un tableau de chaine de caractere ou bien directement passer son adresse ena rgument car je vais déréférencer l'adresse en local quoi qu'il arrive et la parcourir. Désolé mais je rame vraiment pour l'instant ^^'

    EDIT2: Alors j'ai completement changer le comportement de ft_strdel car je pensais que free() ne libéré qu'une case mémoire et donc que pour free une string il fallait free chaque case. j'ai essayé avec le code suivant et il semblerait que j'ai le résultat voulu.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #include "libft.h"
     
    void	ft_strdel(char **as)
    {
    	free(*as);
    	*as = NULL;
    }

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include "libft.h"
    #include <stdio.h>
    #include <stdlib.h>
     
    int main()
    {
        char *s_test = malloc(sizeof(char) * (7));
        char* s_tableau[] = { s_test, NULL };
     
        strcpy(s_test,"coucou");
        ft_strdel(&s_test);
    	printf("%s\n", s_test);
        return (0);
    }
    le resultat est bien "(null)" et je n'ai pas d'erreur à la compilation. Si certains connaissent une technique pour verifier qu'un free() s'est correctement déroulé je suis preneur !

    Merci encore l'ami !

  5. #5
    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
    Après ta modification, tu n'as plus besoin du tableau de pointeurs (d'ailleurs, j'avais mal lu la fonction ft_strdel(), je croyais qu'elle bouclait sur le tableau de pointeurs alors qu'en fait elle bouclait sur les caractères de la chaîne -- donc en fait, le coup du tableau de pointeurs était inutile).
    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.

  6. #6
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2018
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 27
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2018
    Messages : 6
    Points : 6
    Points
    6
    Par défaut
    Je te remercie de ton aide Médinoc !

  7. #7
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 673
    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 673
    Points : 30 962
    Points
    30 962
    Billets dans le blog
    1
    Par défaut
    Bonjour
    Citation Envoyé par P.N.J Voir le message
    Si certains connaissent une technique pour verifier qu'un free() s'est correctement déroulé je suis preneur !
    Devise shadock: s'il n'y a pas de solution, c'est qu'il n'y a pas de problème. Le free() ne se vérifie pas car il réussi toujours.

    En fait, l'explication existe mais amène au même résultat. Quand tu alloues, tu as tout un truc qui se met en branle pour te réserver la zone puis en final un drapeau qui se lève et qui dit "ici c'est réservé". Quand tu libères, le drapeau se baisse et devient "ici c'est libre", rien d'autre. Tu ne vas pas me dire qu'il est nécessaire de tester si l'instruction flag=0 s'est bien déroulée !!!

    Et quand bien même elle se passerait mal et tu le détecterais, que ferais-tu ? Tu dirais à l'utilisateur "désolé la libération n'a pas fonctionné le programme doit s'arrêter" ??? "Ben c'est pas grave, de toute façon le programme se serait arrêté même si elle s'était bien déroulée" te répondra-t-il.
    En revanche, si l'allocation/libération se fait en boucle, et que la libération N se passait (éventuellement) mal, alors fatalement l'allocation N+1 se passerait (là bien concrètement) mal et là tu le verrais...

    Citation Envoyé par P.N.J Voir le message
    et j'écris caractères par caractères car j'avais l'impression que c'était différent de faire s_test = "coucou"
    Oui et non.
    Tu peux écrire char *s="coucou". Dans ce cas, "s" pointe vers une zone statique de ton code, zone qui contient la chaine "coucou". Tu peux donc afficher "s" ou même "s[x]" mais tu ne peux pas modifier "s[x]" (car la zone est non modifiable) et tu peux modifier "s" (dans le sens "le faire pointer vers autre chose") mais dans ce cas, tu as alors perdu toute référence à la zone qui contient "coucou".
    Tu peux aussi écrire char s[]="coucou". Dans ce cas, le compilateur remplace par char s[7]={'c', 'o', 'u', 'c', 'o', 'u', '\0'} (il détecte la taille nécessaire et remplit le tableau selon la syntaxe habituelle des tableaux initialisés à leur création). s est donc un tableau et tu peux accéder et modifier s[x] à ta guise (tant que "x" reste compris entre 0 et 6) mais tu ne peux pas modifier "s" lui-même (le nom d'un tableau est invariant).
    Et tu peux enfin écrire (comme tu l'as fait) char s[100] (tableau figé) ou bien char *s=malloc(100 * sizeof(*s)) (pointeur alloué) puis remplir chaque élément de "s" soit par toi-même, soit par une fonction qui le fera à ta place (tel strcpy() ou memcpy()).

    En revanche, tu ne peux absolument pas écrire (comme je l'ai vu) char *s=malloc(100 * sizeof(*s)) puis s="coucou". En effet, ca fonctionne (il s'agit en fait d'un mix combinant la 3° écriture suivie de la première où tu écrases le pointeur) mais tu perds la zone allouée. Et d'une façon plus générale, si tu écris var=truc puis (sans avoir traité "var") var=autre_chose ça veut dire que soit il y a un souci de conception, soit un souci de compréhension de ce que représente "var".

    Citation Envoyé par P.N.J Voir le message
    mais la pourtant j'ai alloué s_test juste avant.
    Oui mais ensuite tu as remplacé la valeur de s_test (qui contenait l'adresse de la zone allouée) par une autre valeur (l'adresse d'une zone statique).
    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]

  8. #8
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2018
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 27
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2018
    Messages : 6
    Points : 6
    Points
    6
    Par défaut
    Merci Sve@r pour ces explications incroyablement claires !

    Oui, comme tu as pu le remarquer, ma compréhension est encore trop limité et je continue a faire des choses sans vraiment comprendre ou en pensant comprendre des choses fausses, ce qui induit inexorablement des erreurs à la compilation ou à l'execution, mais grace à vous deux j'y vois beaucoup plus claire !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. besoin d'eclaircissement sur malloc et free
    Par Jackyzgood dans le forum C
    Réponses: 8
    Dernier message: 08/02/2010, 16h40
  2. malloc et free pour une liste de 504 bytes
    Par le mage tophinus dans le forum Langage
    Réponses: 2
    Dernier message: 30/10/2008, 11h22
  3. malloc et free en C
    Par aymeric2k dans le forum Bibliothèque standard
    Réponses: 9
    Dernier message: 09/12/2007, 18h04
  4. malloc et free
    Par petdelascar dans le forum C
    Réponses: 6
    Dernier message: 15/01/2006, 21h08
  5. malloc et free
    Par barthelv dans le forum C
    Réponses: 3
    Dernier message: 22/07/2003, 18h34

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