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 :

"double free or corruption (!prev)" pour libération d'un tableau 2D


Sujet :

C

  1. #1
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    mars 2022
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 20
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : mars 2022
    Messages : 5
    Points : 4
    Points
    4
    Par défaut "double free or corruption (!prev)" pour libération d'un tableau 2D
    Bonjour,

    Je me retrouve ici à cause d'un problème qui m'échappe totalement.

    Quand je veux libérer un tableau 2D, j'utilise une fonction "LibereTab2D" codé par mes soins dont voici le code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     void LibereTab2D(long long int **T, int n)
    {
    	if(T != NULL)
        {
    		for(int i=0; i<n; i++)
    		{
    			if (T[i] != NULL)
    			{
    				free(T[i]);
    			}
    		}
    		free(T);
    	}
    }
    Cette fonction, si je l'utilise, il y a presque toujours ce message d'erreur sur mon terminal : "double free or corruption (!prev) make: *** [Makefile:6: launch] Aborted (core dumped)"
    Je dis presque car si je fait un tableau 2D avec un nombre de colonne compris entre 1 et 7, le message d'erreur disparaît ou alors un autre prends sa place.
    (je précise que j'utilise Ubuntu pour compiler mes code sources et les exécuter).
    Ce tableau 2D, je le créé avec cette autre fonction codé là aussi par mes soins :
    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
     
    long long int **creatabLL2D(int ligne, int colonne)
    {
    	long long int   **  Tableau = NULL;
     
    	Tableau = (long long int**)calloc(ligne,sizeof(long long int*));
     
    	if (Tableau == NULL)
    	{
    		return NULL;
    	}
     
    	for(int i=0; i<ligne; i++)
        {
    		Tableau[i] = (long long int*)calloc(colonne,sizeof(long long int));
     
    		if (Tableau[i] == NULL)
    		{
    			for(int j=0; j<i; j++)
    			{
    				free(Tableau[j]);
    			}
    			free(Tableau);
    			return NULL;
    		}
    	}
    	return Tableau;
    }
    Voici un petit main() (pour celles et ceux qui veulent tester) avec les fonctions appelées tel quel dans mon projet :
    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
     
    #include <stdio.h>
    #include <stdlib.h>
     
    int main(){
            int   tailleTabAge, nbMoisTotal;
            long long int   **	tabAge;
     
            printf("Combien de mois voulez vous simuler (par experiences) ? Tapez un entier:");
    	scanf("%d", &nbMoisTotal);
     
            tailleTabAge = nbMoisTotal + 9;
            tabAge = creatabLL2D(2, tailleTabAge);
     
            if(tabAge == NULL)
            {
                    printf("ERREUR D'ALLOCATION DYNAMIQUE !\n");
                    exit(-5);
            }
     
            //manipulation du tableau tabAge
     
            LibereTab2D(tabAge, 2);
            return 0;
    }
    Ma question étant de savoir où est le problème et comment le résoudre. Car malgré plusieurs recherches, il m'est impossible de comprendre comment une erreur pareille peut se générer (sans me vanter, ma fonction LibereTab2D semble être parfaite).

    Si une âme charitable peut éclairer ma lanterne, je lui en serais très reconnaissant.

    (note : Etant nouveau ici, il est possible que j'ai posté le message sur le mauvais forum. Si tel est le cas, faite le mois savoir au plus vite, en vous remerciant d'avance pour la bienveillance)

  2. #2
    Membre averti

    Homme Profil pro
    Enseignant
    Inscrit en
    septembre 2012
    Messages
    264
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : septembre 2012
    Messages : 264
    Points : 310
    Points
    310
    Par défaut
    Ce sont les free() que tu fais dans tes fonctions creatabLL2D() et LibereTab2D() , ...
    A un moment tu veux libérer un espace déjà libéré... il doit y avoir un free() mal placé dans une de tes fonctions...

    J'ai pas essayé mais je pense que c'est en ligne 12: LibereTab2D().

    Dans CreateTab2D()...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    if (Tableau[i] == NULL)
    		{
    			for(int j=0; j<i; j++)
    			{
    				free(Tableau[j]);
    			}
    			free(Tableau);
    			return NULL;
    		}
    ...moi perso je ferais return NULL directement...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if (Tableau[i] == NULL) return NULL;

  3. #3
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    mars 2022
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 20
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : mars 2022
    Messages : 5
    Points : 4
    Points
    4
    Par défaut Après test solution proposée
    Merci beaucoup pour votre réponse rapide.

    Citation Envoyé par hurukan Voir le message
    Dans CreateTab2D()...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    if (Tableau[i] == NULL)
    		{
    			for(int j=0; j<i; j++)
    			{
    				free(Tableau[j]);
    			}
    			free(Tableau);
    			return NULL;
    		}
    ...moi perso je ferais return NULL directement...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if (Tableau[i] == NULL) return NULL;
    En temps normal c'est ce que j'aurais aussi fait. Mais suite au cours de programmation avancé, (et dans un soucis de bien faire) notre professeur nous a dit que pour bien gérer la mémoire, surtout lors de la création d'un tableau, il fallait suivre ces principes :
    -Si les lignes du tableau n'ont pas été créer, retourner NULL
    -Si les lignes ont été créé mais pas la 1ERe colonne, libérer les ligne avec free(Tableau) pour ne pas avoir de l'espace mémoire non désalloué et retourner NULL.
    -Si les lignes ont été créé, ainsi que les 1ere colonnes mais que le processus s'arrête en plein milieu, il faut libérer les colonnes alloués avec free(Tableau[i]) pour j allant de 0 jusqu'à la colonne qui n'a pas pu être alloué, puis libérer les lignes avec free(Tableau). Car si on ne fait pas toute cette procédure, il va rester de l'espace mémoire non désalloué (dû au tableau qui gère les lignes ainsi que les colonnes).

    Edit : Après avoir testé votre solution, le problème ne s'est pas réglé, je pense donc que ça vient de LibereTab2D, mais je ne vois pas comment.

    Ou alors c'est le premier calloc dans creatabLL2D (étant donné que je fais un tableau de pointeur, l'initialiser à 0 n'est peut-être pas la meilleure des idées), mais j'ai testé avec un malloc et le résultat est le même.

    Mon projet ne porte pas sur ça, c'est juste dans le soucis de bien faire encore une fois.

  4. #4
    Membre averti

    Homme Profil pro
    Enseignant
    Inscrit en
    septembre 2012
    Messages
    264
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : septembre 2012
    Messages : 264
    Points : 310
    Points
    310
    Par défaut
    J'ai bien essayé mais je ne sais pas reproduire l'erreur... peu importe la valeur fournie à nbMoisTotal :{

  5. #5
    Membre éprouvé Avatar de edgarjacobs
    Homme Profil pro
    Développeur informatique
    Inscrit en
    mai 2011
    Messages
    481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : mai 2011
    Messages : 481
    Points : 1 210
    Points
    1 210
    Par défaut
    Hello,

    Il se peut que l'erreur de free() provienne de la ligne 21 du main(): on ne sait pas quelles manipulations sont faites.

  6. #6
    Expert éminent
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    juillet 2013
    Messages
    4 225
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : juillet 2013
    Messages : 4 225
    Points : 9 497
    Points
    9 497
    Par défaut
    Non ta fonction n'est pas parfaite il faut coder en mode "brute force".
    Et :
    • évite d'initialiser les variables à la définition ... si quelques lignes + loin tu écrases cette valeur sans utiliser la variable
    • il manque les tests préconditions
    • il existe EXIT_SUCCESS et EXIT_FAILURE : cstdlib section macro constants, lien cplusplus.com en anglais
    • tes noms ne sont pas terribles et surtout tes messages d'erreur sont mauvais : ils n'ont pas d'indication d'où ils viennent


    Exemple de code :
    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
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    #include <stdio.h>
    #include <stdlib.h>
     
     
    #define G_TAB_FREE(TAB, N) \
        for(i=0; i < N; ++i) { if (TAB[i] != NULL) { free(TAB[i]); } } \
         \
        free(TAB); \
        TAB = NULL;
     
     
    void LibereTab2D(long long int*** tab, int n) {
        if ((tab != NULL) && ((*tab) != NULL) && (n > 0)) {
            size_t i;
     
            G_TAB_FREE((*tab), n);
        }
    }
     
     
    void LibereTab2D_bad(long long int** tab, int n) {
        if ((tab != NULL) && (n > 0)) {
            size_t i;
     
            G_TAB_FREE(tab, n);
        }
    }
     
     
    long long int** creatabLL2D(int ligne, int colonne) {
        long long int** ret;
     
        if ((ligne > 0) && (colonne > 0)) {
            ret = calloc(ligne, sizeof(long long int*));
     
            if (ret != NULL) {
                size_t i;
                unsigned has_no_error;
     
                has_no_error = 1;
     
                for(i=0; (has_no_error && (i < ligne)); ++i) {
                    ret[i] = calloc(colonne, sizeof(long long int));
     
                    if (ret[i] == NULL) { has_no_error = 0; }
                }
     
                if (!has_no_error) {
                    G_TAB_FREE(ret, ligne);
                }
            }
        } else {
            ret = NULL;
        }
     
        return ret;
    }
     
     
    int main()
    {
        size_t tailleTabAge, nbMoisTotal;
        long long int** tabAge;
     
    //  printf("Combien de mois voulez vous simuler (par experiences) ? Tapez un entier:");
    //  scanf("%d", &nbMoisTotal);
        nbMoisTotal = 14;
     
        tailleTabAge = nbMoisTotal + 9;
        tabAge = creatabLL2D(2, tailleTabAge);
     
        if (tabAge == NULL) {
            printf("main - error : creatabLL2D\n");
     
            return EXIT_FAILURE;
        }
     
    //  Manipulation du tableau tabAge
    //  LibereTab2D(&tabAge, 2);
        LibereTab2D_bad(tabAge, 2);
     
        printf("main - debug : deallocation %s\n", ((tabAge == NULL)? "successed": "failed")); 
     
        return EXIT_SUCCESS;
    }

  7. #7
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    mars 2022
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 20
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : mars 2022
    Messages : 5
    Points : 4
    Points
    4
    Par défaut
    Citation Envoyé par edgarjacobs Voir le message
    Bonjour, merci aussi de la réponse.
    Je vous liste ci dessous toutes les manipulation faite avec le tableau (ainsi que des explication des fonctions utilisés). Je précise aussi que toutes les fonctions sont dans un autre fichier à part que j'ai joins au fichier main par l'intermédiaire d'un fichier .h (mais le problème m'arrivait aussi quand mes fonctions était encore dans mon main).
    Il y a d'autres manipulations mais elles ne concernent pas ce tableau en particulier.

    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
    //déclaration du tableau + initialisation
                    tabAge[0][8] = male;
    		tabAge[1][8] = femelle;
    //manipulation qui n'ont rien à voir avec tabAge
                                    for(k=8; k<tailleTabAge; k++)
    						{
    							if(tabAge[1][k] > 0)
    							{
    								tabAge[1][k]--;
    								break;
    							}
    						}
    //manipulation qui n'ont rien à voir avec tabAge
                                    femelleInfertile = mortLapin(tabAge, tailleTabAge, &cmptMort);
    				ageLapin(tabAge, tailleTabAge);
    				verifNbrLapin(&femelle, &male, laperaux, tabAge, tailleTabAge);
                                    cmptNaissance = naissance(tabPortee[moisParAn], femelleFertile, tabAge);
     
    //libération du tableau
    mortLapin() va parcourir le tableau et décrémenter les nombre présent dans chaque case
    ageLapin() va décaler toutes les cases d'1 vers la droite, et si c'est la dernière case, ils sont éliminés
    verifNbrLapin() va juste compter les valeurs de chaque cases du tableau
    naissance() va ajouter des valeurs aux cases [0][0] et [1][0]

    Ce sont les seules manipulations différentes que je fais (elles peuvent se répétés) et aucune de ces fonction ne contient la commande free() (j'ai revérifié au cas où)
    Et je rappelle aussi que si j'efface libereTab2D, il n'y a plus aucun message d'erreur.

    Citation Envoyé par foetus Voir le message
    Merci beaucoup pour votre réponse. je vais essayer de l'étudier et de prendre en compte vos remarques

    J'ai testé vos corrections et malheureusement toujours le même problème
    Pourtant j'ai revérifié il me suffit juste d'enlever LibereTab2D pour qu'il n'y ait plus aucun problème.
    Donc le problème viens de ce tableau et en particulier de sa libération.
    Je vous avoue que mon incompréhension est totale.

  8. #8
    Expert éminent
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    juillet 2013
    Messages
    4 225
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : juillet 2013
    Messages : 4 225
    Points : 9 497
    Points
    9 497
    Par défaut
    Citation Envoyé par Annihyls Voir le message
    Donc le problème viens de ce tableau et en particulier de sa libération.
    Je vous avoue que mon incompréhension est totale.
    Je pense que c'est le prototype de ta fonction qui est mauvais j'ai mis à jour mon code.

    Il faut passer le pointeur de ton tableau, parce que sinon tu conserves l'ancienne adresse qui vient d'être libérée (le fameux passage par valeur/ paramètre entrée)

  9. #9
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    mars 2022
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 20
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : mars 2022
    Messages : 5
    Points : 4
    Points
    4
    Par défaut
    Citation Envoyé par foetus Voir le message
    Je pense que c'est le prototype de ta fonction qui est mauvais j'ai mis à jour mon code.

    Il faut passer le pointeur de ton tableau, parce que sinon tu conserves l'ancienne adresse qui vient d'être libérée (le fameux passage par valeur/ paramètre entrée)
    En fait vos solutions fonctionnent très bien dans mon petit main() de test.
    Mais dès que j'essaye de l'implémenter dans mon projet j'ai toujours ce message d'erreur.
    Le problème doit être lié à mes manipulations, même si c'est très bizarre car je ne fait aucun free() dans ces fonctions là. Et surtout qu'il me suffit d'enlever le libereTab2D pour que l'erreur disparaisse.
    Je vais "enquêter" demain, à tête reposée. Vous m'avez donné de bonnes pistes et je compte bien les explorer. Encore merci pour tout.

  10. #10
    Membre éprouvé
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    juillet 2020
    Messages
    309
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : juillet 2020
    Messages : 309
    Points : 1 150
    Points
    1 150
    Par défaut
    Bonjour,
    sinon ce qui est vachement indispensable c'est aussi savoir utiliser des outils qui permettent de debuguer … comme par exemple valgrind.

    En résumé :

    tu compiles ton code en mode debug :
    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
    $ cat dfree.c 
    #include <stdio.h>
    #include <stdlib.h>
     
    int main(void)
    {
        void *dummy=malloc(10000);
     
        // some code
     
        free(dummy);
        free(dummy);
     
        return 0;
    }
    $ gcc -Wall -Wextra -g -o dfree dfree.c 
    $ ./dfree 
    double free or corruption (top)
    Aborted (core dumped)
    Puis tu utilises valgrind pour avoir des infos sur l'erreur :
    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
    $ valgrind --tool=memcheck --leak-check=full --track-origins=yes --show-reachable=yes ./dfree
    ==19774== Memcheck, a memory error detector
    ==19774== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==19774== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
    ==19774== Command: ./dfree
    ==19774== 
    ==19774== Invalid free() / delete / delete[] / realloc()
    ==19774==    at 0x484827F: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==19774==    by 0x109176: main (dfree.c:11)
    ==19774==  Address 0x4a89040 is 0 bytes inside a block of size 10,000 free'd
    ==19774==    at 0x484827F: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==19774==    by 0x10916A: main (dfree.c:10)
    ==19774==  Block was alloc'd at
    ==19774==    at 0x4845899: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==19774==    by 0x10915A: main (dfree.c:6)
    ==19774== 
    ==19774== 
    ==19774== HEAP SUMMARY:
    ==19774==     in use at exit: 0 bytes in 0 blocks
    ==19774==   total heap usage: 1 allocs, 2 frees, 10,000 bytes allocated
    ==19774== 
    ==19774== All heap blocks were freed -- no leaks are possible
    ==19774== 
    ==19774== For lists of detected and suppressed errors, rerun with: -s
    ==19774== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
    Et là comme par magie tu comprends que l'erreur (le second free) a été fait dans la fonction main en ligne 11 ; que c'est une erreur car il y a déjà eu un free sur la même adresse dans main en ligne 10 et que cette adresse a été allouée dans main en ligne 6.

    Tu as tout ce qu'il te faut pour retracer, comprendre et corriger l'erreur …




    Parfois même tu peux demander au compilo de faire une analyse statique de ton code et il arrive qu'il mettre ce genre d'erreur en évidence :
    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
    $ gcc -Wall -Wextra -g -fanalyzer -o dfree dfree.c 
    dfree.c: In function ‘main’:
    dfree.c:11:5: warning: double-‘free’ of ‘dummy’ [CWE-415] [-Wanalyzer-double-free]
       11 |     free(dummy);
          |     ^~~~~~~~~~~
      ‘main’: events 1-3
        |
        |    6 |     void *dummy=malloc(10000);
        |      |                 ^~~~~~~~~~~~~
        |      |                 |
        |      |                 (1) allocated here
        |......
        |   10 |     free(dummy);
        |      |     ~~~~~~~~~~~  
        |      |     |
        |      |     (2) first ‘free’ here
        |   11 |     free(dummy);
        |      |     ~~~~~~~~~~~  
        |      |     |
        |      |     (3) second ‘free’ here; first ‘free’ was at (2)
        |

    Si tu n'as pas valgrind, ou que ta plateforme ne propose tout simplement pas cet outil, tu peux aussi demander au compilo de vérifier au runtime les accès mémoires :
    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
    $ gcc -Wall -Wextra -g -fsanitize=address -o dfree dfree.c 
    $ ./dfree 
    =================================================================
    ==19833==ERROR: AddressSanitizer: attempting double-free on 0x626000000100 in thread T0:
        #0 0x7fa8f0f7ca79 in __interceptor_free /usr/src/debug/gcc/libsanitizer/asan/asan_malloc_linux.cpp:127
        #1 0x5622c0006196 in main /home/fred/Desktop/dfree.c:11
        #2 0x7fa8f0ce830f in __libc_start_call_main (/usr/lib/libc.so.6+0x2d30f)
        #3 0x7fa8f0ce83c0 in __libc_start_main@GLIBC_2.2.5 (/usr/lib/libc.so.6+0x2d3c0)
        #4 0x5622c0006094 in _start (/home/fred/Desktop/dfree+0x1094)
     
    0x626000000100 is located 0 bytes inside of 10000-byte region [0x626000000100,0x626000002810)
    freed by thread T0 here:
        #0 0x7fa8f0f7ca79 in __interceptor_free /usr/src/debug/gcc/libsanitizer/asan/asan_malloc_linux.cpp:127
        #1 0x5622c000618a in main /home/fred/Desktop/dfree.c:10
        #2 0x7fa8f0ce830f in __libc_start_call_main (/usr/lib/libc.so.6+0x2d30f)
     
    previously allocated by thread T0 here:
        #0 0x7fa8f0f7cdd9 in __interceptor_malloc /usr/src/debug/gcc/libsanitizer/asan/asan_malloc_linux.cpp:145
        #1 0x5622c000617a in main /home/fred/Desktop/dfree.c:6
        #2 0x7fa8f0ce830f in __libc_start_call_main (/usr/lib/libc.so.6+0x2d30f)
     
    SUMMARY: AddressSanitizer: double-free /usr/src/debug/gcc/libsanitizer/asan/asan_malloc_linux.cpp:127 in __interceptor_free
    ==19833==ABORTING
    Après il faut lire un peu la doc de ton compilo, de ta plateforme, …
    Et surtout cette compétence indispensable t'évite d'attendre un debugage par forum qui est toujours plus long …

    Edit:

    1. enlever les malloc est une mauvaise idée car cela crée des fuites mémoires ; valgrind signalera aussi ces erreurs ;
    2. ON NE TRANSTYPE JAMAIS LE RETOUR DE MALLOC EN C !

  11. #11
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    mars 2022
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 20
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : mars 2022
    Messages : 5
    Points : 4
    Points
    4
    Par défaut merci !!!!
    Eh bien merci beaucoup pour ce tuyaux grâce à votre commande, j'ai réussi à identifier le problème et à le corriger.
    J'avais plusieurs problème d'écriture/lecture dans mes tableaux + oubli de libérer deux tableaux.
    C'est en effet vachement plus pratique d'utiliser le debugage.
    Merci encore pour tout, mon problème est résolu !

  12. #12
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    février 2006
    Messages
    10 410
    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 : 10 410
    Points : 28 299
    Points
    28 299
    Billets dans le blog
    1
    Par défaut
    Bonjour
    Citation Envoyé par Annihyls Voir le message
    En temps normal c'est ce que j'aurais aussi fait.
    C'est mal. Surtout en temps normal on fait les choses correctement.

    Citation Envoyé par Annihyls Voir le message
    Mais suite au cours de programmation avancé, (et dans un soucis de bien faire) notre professeur nous a dit que pour bien gérer la mémoire, surtout lors de la création d'un tableau, il fallait suivre ces principes :
    -Si les lignes du tableau n'ont pas été créer, retourner NULL
    -Si les lignes ont été créé mais pas la 1ERe colonne, libérer les ligne avec free(Tableau) pour ne pas avoir de l'espace mémoire non désalloué et retourner NULL.
    -Si les lignes ont été créé, ainsi que les 1ere colonnes mais que le processus s'arrête en plein milieu, il faut libérer les colonnes alloués avec free(Tableau[i]) pour j i allant de 0 jusqu'à la colonne ligne qui n'a pas pu être alloué, puis libérer les lignes avec free(Tableau). Car si on ne fait pas toute cette procédure, il va rester de l'espace mémoire non désalloué (dû au tableau qui gère les lignes ainsi que les colonnes).
    C'est exact. Et effectivement c'est ce qu'il faut faire. Mais tu te rends compte que c'est chiant (et tu as raison aussi).
    Toutefois, puisque tu as une fonction qui libère le tableau en temps normal, pourquoi tu ne t'en sers pas aussi quand tu dois libérer le tableau en cas d'erreur d'alloc?
    Exemple
    1) tu crées ta fonction de libération du tableau
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void libereTab2D(long long int **T, size_t n) {
    	// Gestion des cas particuliers
    	if (T == NULL) return;
     
    	// Ok, maintenant on peut coder sans perdre bêtement une tabulation
    	for (size_t i=0; i < n; i++) free(T[i])		// On s'en fout si T[i] vaut NULL car free(NULL) est autorisé. Mais comme on va le voir, ce ne sera jamais le cas
    	free(T);
    }

    2) tu crées ta fonction de création du tableau
    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
    long long int **createTab2D(size_t lig, size_t col) {
    	long long int **T=malloc(lig * sizeof(*T));		// Même pas besoin de calloc (pourquoi mettre toutes les adresses à 0 vu qu'on va les remplir ensuite)
    	if (T == NULL) return NULL;
     
    	for (size_t i=0; i < lig; i++) {
    		T[i]=calloc(col, sizeof(**T));			// Ok là calloc si nécessaire (peut-être que le client veut toutes les cases à 0)
    		if (T[i] == NULL) {
    			libereTab2D(T, i);			// On ne libère que ce qui a été réellement alloué (et qui bien évidemment ne vaut pas NULL ce qui évite de devoir tester ce cas)
    			return NULL;
    		}
    	}
     
    	return T;
    }

    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
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    #include <stdio.h>
    #include <stdlib.h>
     
    void libereTab2D(long long int **T, size_t n) {
    	if (T == NULL) return;
     
    	for (size_t i=0; i < n; i++) {
    		printf("libération %d\n", i);
    		free(T[i]);	
    	}
    	free(T);
    }
     
    long long int **createTab2D(size_t lig, size_t col) {
    	long long int **T=malloc(lig * sizeof(*T));
    	if (T == NULL) return NULL;
     
    	for (size_t i=0; i < lig; i++) {
    		T[i]=calloc(col, sizeof(**T));
    		if (T[i] == NULL) {
    			libereTab2D(T, i);
    			return NULL;
    		}
    	}
     
    	return T;
    }
     
    int main() {
    	size_t lig=3;
    	size_t col=5;
     
    	// Allocation
    	long long int **T=createTab2D(lig, col);
    	if (T == NULL) return -1;
     
    	// Remplissage
    	size_t i, j;
    	for (i=0; i < lig; i++) {
    		for (j=0; j < col; j++) {
    			T[i][j]=(i+1)*(j+1);
    		}
    	}
     
    	// Affichage
    	for (i=0; i < lig; i++) {
    		for (j=0; j < col; j++) {
    			printf("lig=%d, col=%d, T=%d\n", i, j, T[i][j]);
    		}
    	}
     
    	// Libération
    	libereTab2D(T, lig);
    }
    (et si tu veux tester la libération en cas d'erreur te suffit de remplacer T[i]=calloc(...) par T[i]=NULL à un moment de ta boucle)

    Autre solution: un tableau 2D ce n'est rien d'autre qu'une grosse suite de cases qui se suivent en mémoire. Pourquoi alors faire de la 2D si 1D suffit ???
    1) tu crées ta fonction de création du tableau
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    long long int *createTab2D(size_t lig, size_t col) {
    	long long int *T=calloc(lig * col, sizeof(*T));
    	return T;						// Simple et direct
    }

    2) ta fonction de libération sera aussi assez drastique
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    void libereTab2D(long long int *T) {
    	free(T);
    }

    Simplement c'est dans l'adressage qu'il faudra jouer. Chaque case T[i][j] deviendra T[i*nb_col+j] (ce qui est d'ailleurs la façon qu'à le C de traduire en interne un adressage nD en adressage 1D). Et quand tu balayeras ton tableau, le "i" de chaque T[i] donnera lig=i/nb_col et col=i%nb_col. Tu peux même mettre ça en macro pour ne plus t'en préoccuper ensuite.
    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>
     
    long long int *createTab2D(size_t lig, size_t col) {
    	long long int *T=calloc(lig * col, sizeof(*T));
    	return T;
    }
     
    void libereTab2D(long long int *T) {
    	free(T);
    }
     
    #define to1D(i, j, col)				((i)*(col) + (j))
    #define toLig(i, col)				((i) / (col))
    #define toCol(i, col)				((i) % (col))
     
    int main() {
    	size_t lig=3;
    	size_t col=5;
     
    	// Allocation
    	long long int *T=createTab2D(lig, col);
    	if (T == NULL) return -1;
     
    	// Remplissage
    	size_t i, j;
    	for (i=0; i < lig; i++) {
    		for (j=0; j < col; j++) {
    			T[to1D(i, j, col)]=(i+1)*(j+1);
    		}
    	}
     
    	// Affichage
    	for (i=0; i < lig*col; i++) {
    		printf("lig=%d, col=%d, T=%d\n", toLig(i, col), toCol(i, col), T[i]);
    	}
     
    	// Libération
    	libereTab2D(T);
    }
    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

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

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

    Informations forums :
    Inscription : septembre 2005
    Messages : 27 289
    Points : 41 227
    Points
    41 227
    Par défaut
    Donc tu as l'option d'allouer N+1 tableaux, et Sve@r a proposé l'option d'allouer un seul tableau (au prix de la perte de la notation table[y][x])

    Une option entre les deux, c'est d'allouer deux tableaux:
    https://www.developpez.net/forums/d2...e/#post1803870
    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.

  14. #14
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    février 2006
    Messages
    10 410
    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 : 10 410
    Points : 28 299
    Points
    28 299
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Une option entre les deux, c'est d'allouer deux tableaux
    Si j'ai bien pigé, tu crées un tableau 1D comme je le montre, puis tu crées un second tableau lui en 2D qui va pointer sur certaines cases clefs du premier (les premières de chaque ligne en fait).
    Ainsi tu conserves la facilité de l'allocation unique (enfin deux dans ce cas mais deux c'est pas 2000) et tu gardes la notation 2D

    Voici ta méthode adaptée à mes exemples.
    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
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    #include <stdio.h>
    #include <stdlib.h>
     
    long long int **createTab2D(size_t lig, size_t col) {
    	// Allocation zone
    	long long int *zone=calloc(lig * col, sizeof(*zone));
    	if (zone == NULL) return NULL;
     
    	// Allocation des pointeurs de référence
    	long long int **ref=malloc(lig * sizeof(*ref));
    	if (ref == NULL) {
    		free(zone);
    		return NULL;
    	}
     
    	// Association référence avec zone
    	for (size_t i=0; i < lig; i++)
    		ref[i]=zone + col*i;
     
    	return ref;
    }
     
    void libereTab2D(long long int **T) {
    	free(T[0]);
    	free(T);
    }
     
    int main() {
    	size_t lig=3;
    	size_t col=5;
     
    	// Allocation
    	long long int **T=createTab2D(lig, col);
    	if (T == NULL) return -1;
     
    	// Remplissage
    	size_t i, j;
    	for (i=0; i < lig; i++) {
    		for (j=0; j < col; j++) {
    			T[i][j]=(i+1)*(j+1);
    		}
    	}
     
    	// Affichage
    	for (i=0; i < lig; i++) {
    		for (j=0; j < col; j++) {
    			printf("lig=%d, col=%d, T=%d\n", i, j, T[i][j]);
    		}
    	}
     
    	// Libération
    	libereTab2D(T);
    }
    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

  15. #15
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    juin 2010
    Messages
    6 963
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : juin 2010
    Messages : 6 963
    Points : 32 080
    Points
    32 080
    Billets dans le blog
    4
    Par défaut
    plutôt que d'allouer un second tableau, il serait plus efficace de créer une simple fonction pour réaliser l'offset imo
    on perdrait la syntaxe tab[i][j] pour une syntaxe get(tab, i, j)
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  16. #16
    Membre éprouvé
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    juillet 2020
    Messages
    309
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : juillet 2020
    Messages : 309
    Points : 1 150
    Points
    1 150
    Par défaut
    Mouais, en général on n'utilise jamais ce genre de «tableau 2D». On utilisera préférentiellement une structure adaptée au problème à résoudre. Si on tient «absolument» à utiliser un «tableau 2D» autant utiliser un pointeur sur un tableau de tableau en se trimbalant les tailles des «dimensions» ; quelque chose du genre :

    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
    #include <stdio.h>
    #include <stdlib.h>
     
    void matrix_init(size_t row, size_t col, long long (*matrix)[row][col]);
    void matrix_display(size_t row, size_t col, long long (*matrix)[row][col]);
     
    int main(void) {
        size_t row = 13;
        size_t col = 37;
        long long (*matrix)[row][col] = malloc( sizeof *matrix );
     
        matrix_init(row, col, matrix);
        matrix_display(row, col, matrix);
     
        free(matrix);
     
        return 0;
    }
     
    void matrix_init(size_t row, size_t col, long long (*matrix)[row][col]) {
        for(size_t r=0; r<row; r++)
            for(size_t c=0; c<col; c++)
                if (r+c<42)
                    (*matrix)[r][c]=-1;
                else if (r+c>42)
                    (*matrix)[r][c]=1;
                else
                    (*matrix)[r][c]=0;
    }
     
    void matrix_display(size_t row, size_t col, long long (*matrix)[row][col]) {
        for(size_t r=0; r<row; r++) {
            for(size_t c=0; c<col; c++)
                printf("%4Ld ", (*matrix)[r][c]);
            putchar('\n');
        }
    }

  17. #17
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    février 2006
    Messages
    10 410
    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 : 10 410
    Points : 28 299
    Points
    28 299
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Bousk Voir le message
    il serait plus efficace de créer une simple fonction pour réaliser l'offset imo
    Tu réveilles là des discussions plus anciennes sur "vaut-il mieux mémoriser n valeurs ou vaut-il mieux les recalculer à la demande (car c'est en fait ce que fait le code de Médinoc => il mémorise n début de ligne pour ne pas avoir à les recalculer à chaque appel).

    Mais l'informatique évolue. Avant, on était un peu à la ramasse question RAM et toute économie était bon à prendre. Aujourd'hui, les portables sont à 8Go et les tours à 16Go en standard. A mon avis on peut peut-être commencer à se lâcher un peu ; surtout que le second tableau n'est qu'un tableau de pointeurs => il aurait la même taille même si le tableau principal était un tableau de structures...
    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

  18. #18
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    juin 2010
    Messages
    6 963
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : juin 2010
    Messages : 6 963
    Points : 32 080
    Points
    32 080
    Billets dans le blog
    4
    Par défaut
    Ça dépend dans quel secteur tu travailles.
    Même si ça évolue, ça reste limité et pour nous il n'est pas rare qu'on manque de RAM à un moment du projet et qu'on doive aller à la pêche à tous ces petits défauts qu'on a ignoré ou pour lesquels les gens se sont dits "c'est bon, une XOne c'est X GO de ram, on est large" - sauf que ça se remplit vite quand les artistes etc commencent à ajouter du contenu, et souvent les gens ne se disent même pas ça du tout en fait.
    Sans compter la fragmentation de la mémoire si tu en fais un peu partout qui peut avoir un coût plus tard.
    À l'inverse, un calcul d'offset c'est négligeable et tant que tu parcours ça dans le bon ordre tu auras aucun / très peu de cache-miss.

    M'enfin ça dépasse clairement le sujet et l'op.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  19. #19
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    février 2006
    Messages
    10 410
    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 : 10 410
    Points : 28 299
    Points
    28 299
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Bousk Voir le message
    Ça dépend dans quel secteur tu travailles.
    Exact. C'est vrai que le C c'est aussi de l'embarqué et autres minitrucs pour lequel la RAM ça compte. Mais n'étant pas dans ce cas, et vu que j'aime bien la méthode Médinoc, j'y penserai la prochaine fois que j'aurai de la 2D à faire en C.

    Citation Envoyé par Bousk Voir le message
    M'enfin ça dépasse clairement le sujet et l'op.
    Mouais. Lui je pense que maintenant qu'il a eu sa solution il n'a plus envie de revenir. Mais d'autres peuvent s'intéresser au débat
    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

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

Discussions similaires

  1. Réponses: 8
    Dernier message: 17/05/2019, 17h27
  2. [ProFTPd][glibc detected double free or corruption]
    Par Théolude dans le forum Administration système
    Réponses: 1
    Dernier message: 28/08/2008, 09h19
  3. [SFML] Image double free or corruption
    Par Belegkarnil dans le forum SFML
    Réponses: 4
    Dernier message: 23/08/2007, 16h56
  4. erreur glibc detected double free or corruption.
    Par Screwt-K dans le forum C++
    Réponses: 1
    Dernier message: 02/07/2007, 16h46
  5. Problème d'éxécution: double free or corruption
    Par ciol_tebroc dans le forum C++
    Réponses: 3
    Dernier message: 17/05/2006, 19h44

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