qsort segfault in C

人盡茶涼 提交于 2019-12-12 10:17:41

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!