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 :

Problème mémoire inexpliquable


Sujet :

C

  1. #1
    Nouveau candidat au Club
    Inscrit en
    Août 2011
    Messages
    2
    Détails du profil
    Informations forums :
    Inscription : Août 2011
    Messages : 2
    Par défaut Problème mémoire inexpliquable
    Bonjour à tous,

    J'ai un gros problème mémoire que je n'arrive pas expliquer (depuis 10 heures intenses en recherches et tests). J'ai réduit mon code au minimum pour que vous puissiez m'aider je l'espère.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    typedef struct e{
        int i;
        struct e *svt;
    } element;
     
    typedef element *liste;
    Il s'agit ici d'une simple liste chainée.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    void init(liste *P){
    	*P=NULL;
    }
    Initialisation toute bête...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    void supp(liste *P){
        element* tmp=*P;
        *P=tmp->svt;
        free(tmp);
    }
    On supprime ici l’élément en tête de liste. On retiens donc cet élément pour pouvoir le supprimer proprement avec free et le second élément devient le premier de la liste.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    element* ajout(liste *P,int i){
        element* nouveau=malloc(sizeof(element));
        nouveau->i=i;
        nouveau->svt=NULL;
        if(*P==NULL) // La liste est vide...
            *P=nouveau;
        else{
            nouveau->svt=*P;
            *P=nouveau;
        }
        return nouveau;
    }
    On ajoute un élément à la liste...

    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
     
    int main()
    {
        const int tours=1000000;
        int i=0;
    	liste Maliste;
    	init(&Maliste);
     
        printf("Observation 1: Observez la memoire utilisee avant de commencer\n");
        Sleep(5000);
     
        for(i=0;i<=tours;i++){
            ajout(&Maliste,i);
            supp(&Maliste);
        }
     
        printf("Observation 2: La memoire devrait toujours etre identique\n");
        Sleep(5000);
     
        for(i=0;i<=tours;i++)ajout(&Maliste,i);
        for(i=0;i<=tours;i++)supp(&Maliste);
     
        printf("Observation 3: Fuite memoire observable\n");
        Sleep(5000);
    	return EXIT_SUCCESS;
    }
    Et voila notre main révélateur. La première observation retourne la même quantité de mémoire utilisée que la seconde (chez moi 332K).

    La troisième observation par contre est révélatrice d'une fuite mémoire, puisque pour la même opération on obtiens cette fois 948K de mémoire utilisée.

    Fichier source complet: main.c

    Merci beaucoup pour votre aide.

  2. #2
    Expert confirmé

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Billets dans le blog
    2
    Par défaut
    D'une part dans init et dans insertion tu n'a pas mis en entrée un double pointeur, or tu modifies le pointeur P. Ce doit donc être un doble pointeur.

    D'autre part dans l'insertion tu t'es trompé :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
        else{
            nouveau->svt=*P;
            *P=nouveau;
        }
    devrait plutôt être :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
        else{
            *P->svt=nouveau;
            *P=nouveau;
        }

  3. #3
    Membre émérite
    Avatar de f-k-z
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2006
    Messages
    403
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Mayenne (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Juin 2006
    Messages : 403
    Par défaut
    Bonjour,

    J'ai regardé le code et personnellement rien ne me choque. Avec une analyse via valgrind, j'ai autant d'allocation que de libération. Tu utilises quelle méthode pour faire tes tests de mémoire ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
        else{
            *P->svt=nouveau;
            *P=nouveau;
        }
    y a pas un soucis là Souviron34 ? tu donnes une valeur à *P->svt mais celle-ci va être perdu juste après vu que tu changes *P

    Sinon pour l'appel à init et ajout, beuh on a bien un double pointeur on appel avec &p et on reçoit dans un *p ...

    ++

    Fiki

  4. #4
    Expert confirmé
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Par défaut
    Je ne crois pas que ton code pose un problème de fuite mémoire. Ta méthode pour le savoir n'est pas fiable : la quantité mémoire que tu mesures n'est pas révèlatrice de la mémoire effectivement utilisée par ton programme mais de celle que le système lui concède (il récupérera la mémoire inutilisée selon son bon plaisir).

    Par contre, le code est critiquable sur quelques points :

    1- Le succès des allocations dynamiques n'est pas testé. En cas d'échec, le programme va planter sans avertissement.

    2- C'est une mauvaise idée que de cacher des pointeurs dans un typedef (d'où la remarque en fait non fondée de souviron34 : "D'une part dans init et dans insertion tu n'a pas mis en entrée un double pointeur,...")

    On arrive à des écritures qui ne sont pas logiques. Par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void supp(liste *P){
        element* tmp=*P;
        *P=tmp->svt;
        free(tmp);
    }
    le code est obscur : element* tmp=*P n'est pas logique (et rend obsolete le typedef). Logiquement, on attend liste tmp=*P;.
    Je verrai plutôt une structure de données comme celle-ci (qui ne change rien en fait dans le code mais à mon sens améliore sa clarté)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    typedef struct 
    {
       element * tete;
    }liste;
    et on aura :
    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
    void init(liste *P)
    {
    	P->tete=NULL;
    } 
     
    void supp(liste *P)
    {
        element* tmp = P->tete;
        if(tmp!=NULL)  // ceci doit être testé sinon on plante sur une liste vide
        {
           P->tete = tmp->svt;
           free(tmp);
        }
    }
     
    element* ajout(liste *P,int i)
    {
        element* nouveau=malloc(sizeof(element));
        if(nouveau != NULL) // testé la réussite de l'allocation
        {
           nouveau->i = i;
           nouveau->svt = NULL;
           if(P->tete == NULL) // La liste est vide...
              P->tete = nouveau;
           else
           {
              nouveau->svt = P->tete;
              P->tete = nouveau;
           }
        }
        return nouveau;
    }
    Cette dernière fonction est visiblement simplifiable en
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    element* ajout(liste *P,int i)
    {
        element* nouveau = malloc(sizeof(element));
        if(nouveau != NULL) // test de la réussite de l'allocation
        {
           nouveau->i = i;
           nouveau->svt = P->tete;
           P->tete = nouveau;
        }
        return nouveau;
    }

  5. #5
    Nouveau candidat au Club
    Inscrit en
    Août 2011
    Messages
    2
    Détails du profil
    Informations forums :
    Inscription : Août 2011
    Messages : 2
    Par défaut
    Merci beaucoup à vous tous et un merci particulier à diogene pour le code propre.

    En fait je travaille sous Microsoft Windows et j'utilise le Gestionnaire des tâches pour vérifier la consommation de mémoire.

    D'habitude il me fournit un résultat très cohérent, mais la on peut observer de bons écarts:

    Observation 1:


    Observation 2:


    Observation 3:


    Je ne comprends pas pourquoi le gestionnaire des taches ne veut pas afficher la mémoire effectivement utilisée par le programme et non pas une donnée qui engloberait aussi la mémoire 'que le système concède au programme' (diogene).

    Y a t'il un outils précis pour Windows qui permettrait de mettre en avant la mémoire effectivement utilisée par le programme et donc de pouvoir détecter efficacement un problème de fuites ?

    Merci beaucoup.

  6. #6
    Membre émérite
    Avatar de f-k-z
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2006
    Messages
    403
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Mayenne (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Juin 2006
    Messages : 403
    Par défaut
    Bonsoir,

    soit tu utilises valgrind sous linux (gratuit libre et tout et tout)
    soit tu te tournes vers un logiciel payant comme purify :/ ( http://www-01.ibm.com/software/awdtools/purify/ et http://www-01.ibm.com/software/awdtools/purify/win/)
    soit tu prend les outils microsoft http://support.microsoft.com/kb/268343/fr ou http://msdn.microsoft.com/fr-fr/library/x98tx3cf.aspx

    ++

    Fiki


    //edit correction de boulette dans le post

Discussions similaires

  1. Problème mémoire, c'est grave là :/
    Par gamerome dans le forum C++
    Réponses: 6
    Dernier message: 12/08/2005, 12h29
  2. [CR9] [VB.NET] problème mémoire
    Par prophetky dans le forum SDK
    Réponses: 1
    Dernier message: 26/05/2005, 08h36
  3. Problème mémoire
    Par charliejo dans le forum MFC
    Réponses: 8
    Dernier message: 13/04/2005, 13h45
  4. Problémes mémoire avec le bde sur des bases paradox
    Par Keke des Iles dans le forum Bases de données
    Réponses: 2
    Dernier message: 27/05/2004, 16h55
  5. Problème mémoire avec une dll par chargement dynamique
    Par widze19 dans le forum C++Builder
    Réponses: 6
    Dernier message: 15/12/2003, 13h20

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