问题
I'm trying to use qsort in accordance with the man page but regardless of what I try I keep getting a segfault
Here's the section of code that matters
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;
}
When running gdb I see the list of files in the directory per the fprintf in the while loop and I see the last entry. I placed a breakpoint in compare which is executed N times where N is the number of files and then I immediately get a SEGFAULT from _qsort.
On the Nth call to compare_dirent from qsort it crashes.
Here is the gdb output
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 ()
The solution (complete) was a little of both answers
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;
}
回答1:
One of your problems is:
qsort(table, entry_num, sizeof(struct dirent), &compare_dirent);
should be:
qsort(table, entry_num, sizeof(struct dirent *), &compare_dirent);
The third element is width, the size of each object. Since table is an array of struct dirent *, that is what you want the size of.
You're also misusing the value returned by readdir. From the docs:
The pointer returned by readdir() points to data which may be overwritten by another call to readdir() on the same directory stream.
That means it's quite possibly all of the values in your table are the same pointer, with the same value. You can use readdir_r, or just allocate struct dirent (one for each readdir call), and memcpy the value in place.
An alternative is to change it to be an array of struct dirent, in which case you would use your original qsort call.
回答2:
Your code doesn't save the dirents! You allocate memory to hold pointers to them, but then you never allocate any memory to hold the actual entries. So when you go to sort, your pointers point to objects that no longer exist.
This line of code is broken:
table[entry_num] = entries;
This saves the pointer that points to the dirent now. But that pointer is meaningless later when it no longer points to the dirent. You can't save the pointer to use later, you have to save the actual entry.
The data returned by readdir() may be overwritten by subsequent calls to readdir() for the same directory stream.
So you need something like:
table[entry_name] = malloc(sizeof(struct dirent));
memcpy(table[entry_name], entries, sizeof(struct dirent));
Don't forget to free them when you're done.
Matthew Flaschen's answer is also correct. You passed the wrong size to qsort.
来源:https://stackoverflow.com/questions/10081370/qsort-segfault-in-c