1

我正在尝试根据手册页使用 qsort 但无论我尝试什么,我都会不断收到段错误

这是重要的代码部分

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;
}

运行 gdb 时,我在 while 循环中看到目录中的文件列表,fprintf并看到最后一个条目。我在比较中放置了一个断点,该断点执行 N 次,其中 N 是文件数,然后我立即从 _qsort 获得一个 SEGFAULT。

在从 qsort 第 N 次调用 compare_dirent 时,它崩溃了。

这是gdb输出

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 ()

解决方案(完整)是两个答案中的一小部分

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;
}
4

2 回答 2

5

您的问题之一是:

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

应该:

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

第三个元素是width,每个对象的大小。由于table是 的数组struct dirent *,这就是您想要的大小。

您还滥用了 readdir 返回的值。从文档

readdir() 返回的指针指向可能被同一目录流上的另一个 readdir() 调用覆盖的数据。

这意味着很可能表中的所有值都是相同的指针,具有相同的值。您可以使用readdir_r,或者只是分配struct dirent(每个 readdir 调用一个),然后将值存储在适当的位置。

另一种方法是将其更改为 的数组struct dirent,在这种情况下,您将使用原始qsort调用。

于 2012-04-10T00:03:55.077 回答
3

您的代码不保存dirents! 您分配内存来保存指向它们的指针,但是您永远不会分配任何内存来保存实际条目。因此,当您进行排序时,您的指针指向不再存在的对象。

这行代码坏了:

table[entry_num] = entries;

这将保存指向dirent now的指针。但是当它不再指向dirent. 您无法保存指针以供以后使用,您必须保存实际条目。

readdir() 返回的数据可能会被后续对同一目录流的 readdir() 调用覆盖。

所以你需要类似的东西:

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

完成后不要忘记释放它们。

Matthew Flaschen 的回答也是正确的。您将错误的尺寸传递给qsort.

于 2012-04-10T00:04:17.873 回答