2012-04-09 12 views
1

Ich versuche, in Übereinstimmung mit der man-Seite zu verwenden qsort aber unabhängig davon, was ich versuche, ich erhalte eine segfaultqsort segfault in C

Hier ist der Codeabschnitt, der

int compare_dirent(const void *a, const void *b) 
{ 
    const struct dirent *first = (const struct dirent *) a; 
    const struct dirent *second = (const struct dirent *) b; 

    return first->d_ino - second->d_ino; 
} 


int process(FILE* output,const char *dirname, int flags) 
{ 
    struct dirent *entries = NULL; 
    struct dirent *table[256]; 
    int entry_num = 0; 
    DIR *directory = NULL; 
    char cwd[1024]; 

    getcwd(cwd,1024); 
    bzero(table,256); 

    directory = opendir(dirname); 
    while((entries = readdir(directory))!=NULL) 
    { 
     if(entries->d_type == DT_REG) 
     { 
      fprintf(output,"%s\t\n",entries->d_name); 
      table[entry_num] = entries; 
      entry_num++; 
     } 
    } 
    fprintf(stderr,"last entry: %s\n", table[entry_num-1]->d_name); 

    /* RIGHT HERE */ 
    qsort(table, entry_num, sizeof(struct dirent), &compare_dirent); 

    return entry_num; 
} 

zählt, wenn running gdb Ich sehe die Liste der Dateien im Verzeichnis per fprintf in der while-Schleife und ich sehe den letzten Eintrag. Ich habe einen Breakpoint im Vergleich platziert, der N mal ausgeführt wird, wobei N die Anzahl der Dateien ist und ich dann sofort einen SEGFAULT von _qsort bekomme.

Beim N-ten Aufruf von compare_dirent von qsort stürzt es ab. Hier

ist der GDB Ausgang

Starting program: /Users/luke/Documents/Dev/code/cs647/prog2/bin/prog2 ./ 
Reading symbols for shared libraries +........................ done 
.main.c.swp 
get_pdf.sh 
main.c 
main.o 
Makefile  
program2.pdf  
test.txt  
last entry: test.txt 

Breakpoint 1, compare_dirent (a=0x7fff5fbff018, b=0x7fff5fbfec00) at main.c:88 
88  const struct dirent *first = (const struct dirent *) a; 
(gdb) n 
89  const struct dirent *second = (const struct dirent *) b; 
(gdb) n 
91  return first->d_ino - second->d_ino; 
(gdb) c 
Continuing. 

Breakpoint 1, compare_dirent (a=0x7fff5fbff430, b=0x7fff5fbfec00) at main.c:88 
88  const struct dirent *first = (const struct dirent *) a; 
(gdb) c 
Continuing. 

Breakpoint 1, compare_dirent (a=0x7fff5fbff848, b=0x7fff5fbfec00) at main.c:88 
88  const struct dirent *first = (const struct dirent *) a; 
(gdb) c 
Continuing. 

Breakpoint 1, compare_dirent (a=0x7fff5fbffc60, b=0x7fff5fbfec00) at main.c:88 
88  const struct dirent *first = (const struct dirent *) a; 
(gdb) c 
Continuing. 

Breakpoint 1, compare_dirent (a=0x7fff5fc00078, b=0x7fff5fbfec00) at main.c:88 
88  const struct dirent *first = (const struct dirent *) a; 
(gdb) c 
Continuing. 

Breakpoint 1, compare_dirent (a=0x7fff5fc00490, b=0x7fff5fbfec00) at main.c:88 
88  const struct dirent *first = (const struct dirent *) a; 
(gdb) c 
Continuing. 

Program received signal EXC_BAD_ACCESS, Could not access memory. 
Reason: KERN_PROTECTION_FAILURE at address: 0x00007fff5fc00490 
0x00007fff8e238540 in _qsort() 

Die Lösung (komplett) war ein wenig von beiden Antworten

int compare_dirent(const void *a, const void *b) 
{ 
    const struct dirent *first = (const struct dirent *) a; 
    const struct dirent *second = (const struct dirent *) b; 

    return first->d_ino - second->d_ino; 
} 


int process(FILE* output,const char *dirname, int flags) 
{ 
    struct dirent *entries = NULL; 
    struct dirent table[256]; 
    int entry_num = 0; 
    DIR *directory = NULL; 
    char cwd[1024]; 

    getcwd(cwd,1024); 
    bzero(table,256); 

    directory = opendir(dirname); 
    while((entries = readdir(directory))!=NULL) 
    { 
     if(entries->d_type == DT_REG) 
     { 
      fprintf(output,"%s\t\n",entries->d_name); 
      memcpy(table+entry_num, entries, sizeof(struct dirent)); 
      entry_num++; 
     } 
    } 
    fprintf(stderr,"size: %lu\n", sizeof(struct dirent)); 
    qsort(table, entry_num, sizeof(struct dirent) , compare_dirent); 

    fprintf(output,"\n\n"); 
    for(int i=0;i<entry_num;i++) 
    { 
     fprintf(output,"%s\n", table[i].d_name); 
    } 

    return entry_num; 
} 

Antwort

4

Eines Ihrer Probleme ist:

qsort(table, entry_num, sizeof(struct dirent), &compare_dirent); 

sollte sein:

qsort(table, entry_num, sizeof(struct dirent *), &compare_dirent); 

Das dritte Element ist width, die Größe jedes Objekts. Da table ist ein Array von struct dirent *, das ist, was Sie wollen, die Größe von.

Sie missbrauchen auch den von readdir zurückgegebenen Wert. Von the docs:

Der Zeiger von readdir() zurück verweist auf Daten, die durch einen anderen Aufruf überschrieben werden können() auf dem gleichen Verzeichnis-Stream von readdir.

Das bedeutet, dass es wahrscheinlich alle Werte in Ihrer Tabelle sind die gleichen Zeiger, mit dem gleichen Wert. Sie können readdir_r verwenden oder einfach struct dirent (eine für jeden readdir-Aufruf) zuweisen und den Wert an Ort und Stelle speichern.

Eine Alternative ist, es zu einem Array von struct dirent zu ändern, in diesem Fall würden Sie Ihren ursprünglichen qsort Anruf verwenden.

+0

Das war es, vielen Dank! Ich dachte, ich hätte alles versucht. – lukecampbell

3

Ihr Code speichert nicht die dirent s! Sie reservieren Speicher, um Zeiger auf sie zu halten, aber Sie reservieren dann nie Speicher, um die tatsächlichen Einträge zu enthalten. Wenn Sie also zum Sortieren gehen, zeigen Ihre Zeiger auf Objekte, die nicht mehr existieren.

Diese Codezeile ist gebrochen:

table[entry_num] = entries; 

Dieser den Zeiger speichert, die dirent auf die Punkte jetzt. Aber dieser Zeiger ist später bedeutungslos, wenn er nicht mehr auf dirent zeigt. Sie können den Zeiger nicht speichern, um ihn später zu verwenden. Sie müssen den tatsächlichen Eintrag speichern.

Die von readdir() zurückgegebenen Daten können durch nachfolgende Aufrufe von readdir() für denselben Verzeichnisdatenstrom überschrieben werden.

Sie müssen also so etwas wie:

table[entry_name] = malloc(sizeof(struct dirent)); 
memcpy(table[entry_name], entries, sizeof(struct dirent)); 

Vergessen Sie nicht, um sie zu befreien, wenn Sie fertig sind.

Matthew Flaschen Antwort ist auch richtig. Sie haben die falsche Größe an qsort übergeben.

+0

Danke auch, das war definitiv ein Problem, aber es wurde kein SEGFAULT direkt generiert, aber die Daten waren in der Tat weg und inkorrekt. Ich habe es geändert, um memcpy zu verwenden. Ich werde später zu malloc wechseln. – lukecampbell

+1

@lukecampbell, wo willst du es * zu * memcpy? Der Zeiger muss immer noch auf einen zugewiesenen Bereich zeigen. –

+0

Vielleicht hat er eine Reihe von 'struct dirent' gemacht? –