Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 06/05/2011, à 14:18

rimkat

[RESOLU] [C]probleme avec une fonction récursive

Salut,
Je fais un programme qui a pour but de supprimer les fichiers commençant par '._' généré par MAC OS X pour chaque fichiers musicals. Mon code scan une arborescence de dossier depuis la racine qu'on lui indique. J'ai donc imaginé un traitement via une fonction récursive qui me semblais bien approprié.
Dans un 1er temps, tout  ce passe bien, puis le code plante avec pour erreur 'double free or corruption'. A priori, je ne fais pas de double libération et je ne comprend donc pas pourquoi cela plante.
Si quelqu'un peut m'expliquer pourquoi, je lui serai reconnaissant.

Merci


/*
Ce programme a pour but de supprimer les fichiers commençant par '._' generé par MAC OS X
*/
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <unistd.h>
#include <gtk/gtk.h>

int job(char *chemin);


int job(char *chemin)
{
    DIR * rep = opendir(chemin); 
    
    
    
    if (rep != NULL)
    {
        /*varaible necessaire*/                                             
        struct dirent * ent;
        char recherche []= "._";
        
        while ((ent = readdir(rep)) != NULL)
        {    
            /* si c'est un fichier*/
            if (ent->d_type == 8)
            {
                printf("un fichier   %s\n", ent->d_name);
                /*verifier que la chaine recherché est bien dans le nom du fichier
                si oui supprimer ce fichier*/
                
                if  (strstr(ent->d_name, recherche) != NULL)
                {
                    chdir(chemin);
                    if (remove(ent->d_name) == 0)
                        printf("fichier supprimer\n");
                }
            }
            /* et si c'est un dossier */
            else if (ent->d_type == 4 && (strcmp(ent->d_name, ".")!=0 && strcmp(ent->d_name, "..")!=0))
            {
                /* récuperer le chemin vers ce dossier pour le passer en argument pour l'appel a job */
                printf("un dossier\n");
                char *tmp, temp[50];
                strcpy(temp, "/");
                strcat(temp, ent->d_name);
                tmp = (char *)malloc(strlen(chemin)+strlen(temp));
                if (tmp != NULL)
                {
                    /* copier chemin afin de le garder intact pour le retour en arriere */
                    strcpy(tmp, chemin);
                    strcat(tmp, temp);
                    printf("avant job(), tmp a pour valeur %s et est a l'adresse memoire %p\n", tmp, tmp);
                    job(tmp);
                    printf("apres job(), tmp a pour valeur %s et est a l'adresse memoire %p et va etre libere\n", tmp, tmp);
                    free(tmp);
                }
            }
        }
        
        closedir(rep);
    }
 
    return 0;
}
 
int main(int argc, char *argv[])
{
    char *chemin ;
    GtkWidget *dialog;
    
    gtk_init(&argc, &argv);  
    dialog = gtk_file_chooser_dialog_new ("Open File",
                       NULL,
                       GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                       GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
                       NULL);
                       
     gtk_widget_show_all(dialog);
     
    if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
     {
        chemin = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
        printf("dans if gtk_dialog_run, filename est = à %s\n", chemin);
   }
    gtk_widget_destroy (dialog);

    return job(chemin);
}

Dernière modification par rimkat (Le 07/05/2011, à 20:11)


"On se lasse de tout, excepté d'apprendre"    Virgile

Hors ligne

#2 Le 06/05/2011, à 18:49

Le Farfadet Spatial

Re : [RESOLU] [C]probleme avec une fonction récursive

Salut à tous !

   Si la finalité est juste de supprimer récursivement les fichiers commençant par « ._ », il vaut mieux se pencher vers cette commande :

$ rm ._*

Renseigne-toi sur :

rm -rf

   Ce sera plus vite fait et plus efficace et cela te fera apprendre les commandes Unix.

   S'il s'agit d'un exercice de C, ce n'est pas inintéressant. En revanche, pour t'aider, ce serait bien que tu isoles ton problème. Par exemple, je ne pense pas que tu aies besoin de GTK+ pour reproduire ton problème. Je vais essayer de voir ça, mais dans l'immédiat, je n'ai pas le temps de le compiler. Essaye de faire un exemple complet minimum : tout ce qu'il faut pour reproduire ton problème, mais uniquement ce qu'il faut pour reproduire ton problème. Également, essaye de dérouler une trace d'exécution, en vérifiant ce qui est alloué et ce qui est libéré.

   À bientôt.

Le Farfadet Spatial

Hors ligne

#3 Le 06/05/2011, à 23:11

rimkat

Re : [RESOLU] [C]probleme avec une fonction récursive

Merci pour la réponse

Je connais 'rm' et jusque là je l'utilisais pour supprimer ces fichiers. Ce programme en C est vraiment un exercice.
L'utilisation de Gtk est également un exercice et un apprentissage de cette bibliothèque. En l'occurrence, j'utilise juste le file_chooser pour sélectionner de manière graphique le dossier a scanner.
Pour ce qui est de la trace, j'ai prevu dans mon code des 'printf' pour afficher ce qui est alloué et libéré.

voici les infos que donne le terminal quand mon code plante:

*** glibc detected *** ./dir: double free or corruption (!prev): 0xb1e04070 ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x6b961)[0x77a961]
/lib/i386-linux-gnu/libc.so.6(+0x6d28b)[0x77c28b]
/lib/i386-linux-gnu/libc.so.6(cfree+0x6d)[0x77f41d]
./dir[0x8048d02]
./dir[0x8048cc1]
./dir[0x8048e20]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xe7)[0x725e37]
./dir[0x8048a61]
======= Memory map: ========
00110000-001a5000 r-xp 00000000 08:06 536809     /usr/lib/libgdk-x11-2.0.so.0.2400.4
001a5000-001a6000 ---p 00095000 08:06 536809     /usr/lib/libgdk-x11-2.0.so.0.2400.4
001a6000-001a8000 r--p 00095000 08:06 536809     /usr/lib/libgdk-x11-2.0.so.0.2400.4
001a8000-001a9000 rw-p 00097000 08:06 536809     /usr/lib/libgdk-x11-2.0.so.0.2400.4
001a9000-001c2000 r-xp 00000000 08:06 921608     /usr/lib/i386-linux-gnu/libatk-1.0.so.0.9.1
001c2000-001c3000 ---p 00019000 08:06 921608     /usr/lib/i386-linux-gnu/libatk-1.0.so.0.9.1
001c3000-001c4000 r--p 00019000 08:06 921608     /usr/lib/i386-linux-gnu/libatk-1.0.so.0.9.1
001c4000-001c5000 rw-p 0001a000 08:06 921608     /usr/lib/i386-linux-gnu/libatk-1.0.so.0.9.1
001c5000-00273000 r-xp 00000000 08:06 530507     /usr/lib/libcairo.so.2.11000.2
00273000-00274000 ---p 000ae000 08:06 530507     /usr/lib/libcairo.so.2.11000.2
00274000-00275000 r--p 000ae000 08:06 530507     /usr/lib/libcairo.so.2.11000.2
00275000-00276000 rw-p 000af000 08:06 530507     /usr/lib/libcairo.so.2.11000.2
00276000-00278000 rw-p 00000000 00:00 0 
00278000-0027a000 r-xp 00000000 08:06 936875     /usr/lib/i386-linux-gnu/libXcomposite.so.1.0.0
0027a000-0027b000 r--p 00001000 08:06 936875     /usr/lib/i386-linux-gnu/libXcomposite.so.1.0.0
0027b000-0027c000 rw-p 00002000 08:06 936875     /usr/lib/i386-linux-gnu/libXcomposite.so.1.0.0
0027c000-0027e000 r-xp 00000000 08:06 936879     /usr/lib/i386-linux-gnu/libXdamage.so.1.1.0
0027e000-0027f000 r--p 00001000 08:06 936879     /usr/lib/i386-linux-gnu/libXdamage.so.1.1.0
0027f000-00280000 rw-p 00002000 08:06 936879     /usr/lib/i386-linux-gnu/libXdamage.so.1.1.0
00280000-0029c000 r-xp 00000000 08:06 940797     /lib/i386-linux-gnu/ld-2.13.so
0029c000-0029d000 r--p 0001b000 08:06 940797     /lib/i386-linux-gnu/ld-2.13.so
0029d000-0029e000 rw-p 0001c000 08:06 940797     /lib/i386-linux-gnu/ld-2.13.so
0029e000-002dc000 r-xp 00000000 08:06 923988     /usr/lib/i386-linux-gnu/libpango-1.0.so.0.2800.4
002dc000-002dd000 r--p 0003e000 08:06 923988     /usr/lib/i386-linux-gnu/libpango-1.0.so.0.2800.4
002dd000-002de000 rw-p 0003f000 08:06 923988     /usr/lib/i386-linux-gnu/libpango-1.0.so.0.2800.4
002de000-002e5000 r-xp 00000000 08:06 940770     /lib/i386-linux-gnu/librt-2.13.so
002e5000-002e6000 r--p 00006000 08:06 940770     /lib/i386-linux-gnu/librt-2.13.so
002e6000-002e7000 rw-p 00007000 08:06 940770     /lib/i386-linux-gnu/librt-2.13.so
002e7000-002fc000 r-xp 00000000 08:06 940792     /lib/i386-linux-gnu/libpthread-2.13.so
002fc000-002fd000 r--p 00015000 08:06 940792     /lib/i386-linux-gnu/libpthread-2.13.so
002fd000-002fe000 rw-p 00016000 08:06 940792     /lib/i386-linux-gnu/libpthread-2.13.so
002fe000-00300000 rw-p 00000000 00:00 0 
00300000-00304000 r-xp 00000000 08:06 938569     /usr/lib/i386-linux-gnu/libXfixes.so.3.1.0
00304000-00305000 r--p 00003000 08:06 938569     /usr/lib/i386-linux-gnu/libXfixes.so.3.1.0
00305000-00306000 rw-p 00004000 08:06 938569     /usr/lib/i386-linux-gnu/libXfixes.so.3.1.0
00306000-00313000 r-xp 00000000 08:06 921632     /usr/lib/i386-linux-gnu/libXext.so.6.4.0
00313000-00314000 r--p 0000c000 08:06 921632     /usr/lib/i386-linux-gnu/libXext.so.6.4.0Abandon

Pour reproduire le probleme, il te de suffit de choisir, au lancement de mon programme, un dossier qui contient un grand nombre de sous dossier, qui eux même peuvent contenir leurs propres sous dossiers et eventuellement encore une profondeur.

J'espere que tout cela t'aide a comprendre mon probleme et encore merci de prendre un peu de temps pour m'aider.

@+


"On se lasse de tout, excepté d'apprendre"    Virgile

Hors ligne

#4 Le 07/05/2011, à 10:28

AuraHxC

Re : [RESOLU] [C]probleme avec une fonction récursive

Perso je viens de tester sans regarder ton code et j'obtiens ça en ayant créé un fichier ._trash dans mon dossier images :

10:22 nicolas@AuraHxC ~/Bureau% ./test
Gtk-Message: Failed to load module "canberra-gtk-module"
`menu_proxy_module_load': ./test: undefined symbol: menu_proxy_module_load

(test:5047): Gtk-WARNING **: Failed to load type module: (null)

dans if gtk_dialog_run, filename est = à /home/nicolas/images
un fichier   free_youtube.png
un fichier   ._trash
fichier supprimer
un fichier   binarisation.png
un fichier   Enchantement.jpg
un fichier   free_youtube.jpg

EDIT : Je viens de voir que l'erreur se fait si il y a beaucoup de dossier, je vais donc faire le test et revenir.

EDIT2 : Effectivement, avec un grand nombre de dossier j'obtiens le même résultat mais je ne sais pas pourquoi pour l'instant. Je regarderais dans la journée si j'en ai l'occasion sinon j'espère que Farfadet pourra t'aider wink

Dernière modification par AuraHxC (Le 07/05/2011, à 10:36)

Hors ligne

#5 Le 07/05/2011, à 10:38

Lumin0u

Re : [RESOLU] [C]probleme avec une fonction récursive

juste comme ça, à la ligne:

tmp = (char *)malloc(strlen(chemin)+strlen(temp));

strlen() retourne le nombre de "vrais" caractères, sans compter le '\0' à la fin, donc il te manque 1 case là (sans oublier de mettre cette dernière à '\0').
sinon chez moi ça met: "sYSMALLOc: Assertion [...] failed", il doit y avoir des pistes sur le net là dessus.
et aussi sprintf() est très pratique par rapport à strcpy() + strcat()

Dernière modification par Lumin0u (Le 07/05/2011, à 10:39)

Hors ligne

#6 Le 07/05/2011, à 20:10

rimkat

Re : [RESOLU] [C]probleme avec une fonction récursive

Merci pour vos réponses.

LuminOu, tu avais vu juste le problème venait bien de là. Et merci aussi pour sprintf.

J'ai modifié le code comme ceci:

/*
Ce programme a pour but de supprimer les fichiers commençant par '._' generé par MAC OS X
*/
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <unistd.h>
#include <gtk/gtk.h>

int job(char *chemin);


int job(char *chemin)
{
    DIR * rep = opendir(chemin); 
    
    
    
    if (rep != NULL)
    {
        /*varaible necessaire*/                                             
        struct dirent * ent;
        char recherche []= "._";
        
        while ((ent = readdir(rep)) != NULL)
        {    
            /* si c'est un fichier*/
            if (ent->d_type == 8)
            {
                printf("un fichier   %s\n", ent->d_name);
                /*verifier que la chaine recherché est bien dans le nom du fichier
                si oui supprimer ce fichier*/
                
                if  (strstr(ent->d_name, recherche) != NULL)
                {
                    chdir(chemin);
                    if (remove(ent->d_name) == 0)
                        printf("fichier supprimer\n");
                }
            }
            /* et si c'est un dossier */
            else if (ent->d_type == 4 && (strcmp(ent->d_name, ".")!=0 && strcmp(ent->d_name, "..")!=0))
            {
                /* récuperer le chemin vers ce dossier pour le passer en argument pour l'appel a job */
                printf("un dossier\n");
                char *tmp;
                
                tmp = (char *)malloc(strlen(chemin)+strlen(ent->d_name)+2);
                if (tmp != NULL)
                {
                    /* copier chemin afin de le garder intact pour le retour en arriere */
                    sprintf(tmp, "%s/%s", chemin, ent->d_name);
                    printf("avant job(), tmp a pour valeur %s et est a l'adresse memoire %p\n", tmp, tmp);
                    job(tmp);
                    printf("apres job(), tmp a pour valeur %s et est a l'adresse memoire %p et va etre libere\n", tmp, tmp);
                    free(tmp);
                }
            }
        }
        
        closedir(rep);
    }
 
    return 0;
}
 
int main(int argc, char *argv[])
{
    char *chemin ;
    GtkWidget *dialog;
    
    gtk_init(&argc, &argv);  
    dialog = gtk_file_chooser_dialog_new ("Open File",
                       NULL,
                       GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                       GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
                       NULL);
                       
     gtk_widget_show_all(dialog);
     
    if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
     {
        chemin = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
        printf("dans if gtk_dialog_run, filename est = à %s\n", chemin);
   }
    gtk_widget_destroy (dialog);

    return job(chemin);
}

et cela fonctionne impeccable.

Merci
@+


"On se lasse de tout, excepté d'apprendre"    Virgile

Hors ligne

#7 Le 07/05/2011, à 20:55

Lumin0u

Re : [RESOLU] [C]probleme avec une fonction récursive

je chipote mais tu peux juste mettre +1 au lieu de +2, puisque finalement une concaténation c'est une str normale, donc 1 seul caractère de fin.
et aussi j'ai peut-être parlé un peu vite pour sprintf(), niveau performances ça doit pas être terrible par rapport à strcpy() + strcat(), donc c'est bof pour des programmes qui demandent des perf.

Dernière modification par Lumin0u (Le 07/05/2011, à 21:00)

Hors ligne

#8 Le 07/05/2011, à 21:20

rimkat

Re : [RESOLU] [C]probleme avec une fonction récursive

le +2 c'est pour le "/" que j'ajoute dans sprintf entre chemin et ent->d_name.
Question perfs ce n'est pas trop mal.


"On se lasse de tout, excepté d'apprendre"    Virgile

Hors ligne

#9 Le 08/05/2011, à 16:23

Le Farfadet Spatial

Re : [RESOLU] [C]probleme avec une fonction récursive

Salut à tous !

   Bon, le temps que je repasse par ici, le problème est résolu. Tant mieux. Je rebondis simplement sur deux remarques :

rimkat a écrit :

L'utilisation de Gtk est également un exercice et un apprentissage de cette bibliothèque.

   J'entends bien. Cela dit, ton problème ne venait clairement pas de l'utilisation de cette bibliothèque. Pour présenter un exemple minimal complet, il n'était donc pas nécessaire d'inclure cette bibliothèque. Pour rendre la demande d'aide plus efficace, il est bon de donner un exemple complet et minimal : tout ce qu'il faut pour reproduire le problème, mais uniquement ce qu'il faut pour reproduire le problème.

Pour ce qui est de la trace, j'ai prevu dans mon code des 'printf' pour afficher ce qui est alloué et libéré.

   Je t'invitais plutôt à dérouler l'exécution dans un débogueur. Pour les sorties de ton programme, lorsque tout ce passe bien, il convient de donner uniquement des informations pertinentes pour l'utilisateur, dans ton cas par exemple indiquer les noms des fichiers qui ont été supprimés. Lorsque l'on gère une situation d'erreur, il est bon de tenter de retourner toutes les informations que l'on peut donner qui vont aider à comprendre le problème. En revanche, la phase de débogage doit être la moins intrusive possible, lorsque l'on commence à ajouter de l'affichage un peu à tout va, c'est probablement qu'il est bienvenu d'utiliser un débogueur. Je déconseille de déboguer en ajoutant des instructions que l'on supprimera après, on prend le risque de créer d'autres problèmes.

   À bientôt.

Le Farfadet Spatial

Hors ligne